aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs40
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs93
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs16
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs8
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs8
-rw-r--r--MediaBrowser.Controller/Library/IDirectStreamProvider.cs19
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs6
-rw-r--r--MediaBrowser.Controller/Library/ILiveStream.cs3
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs24
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs6
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs18
-rw-r--r--MediaBrowser.Controller/Library/NameExtensions.cs1
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvService.cs4
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs547
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs23
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs20
-rw-r--r--MediaBrowser.Controller/MediaEncoding/JobLogger.cs5
-rw-r--r--MediaBrowser.Controller/Net/IAuthService.cs3
-rw-r--r--MediaBrowser.Controller/Net/IAuthorizationContext.cs9
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs9
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs8
-rw-r--r--MediaBrowser.Controller/QuickConnect/IQuickConnect.cs15
-rw-r--r--MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs53
-rw-r--r--MediaBrowser.Controller/Security/IAuthenticationManager.cs33
-rw-r--r--MediaBrowser.Controller/Security/IAuthenticationRepository.cs37
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs59
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupMember.cs23
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs11
-rw-r--r--MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs6
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs18
35 files changed, 714 insertions, 426 deletions
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index 8096be1bd..8362db1a7 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -3,8 +3,11 @@
#pragma warning disable CS1591
using System;
+using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Entities.Security;
using Jellyfin.Data.Events;
+using Jellyfin.Data.Queries;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
@@ -16,32 +19,51 @@ namespace MediaBrowser.Controller.Devices
event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
/// <summary>
+ /// Creates a new device.
+ /// </summary>
+ /// <param name="device">The device to create.</param>
+ /// <returns>A <see cref="Task{Device}"/> representing the creation of the device.</returns>
+ Task<Device> CreateDevice(Device device);
+
+ /// <summary>
/// Saves the capabilities.
/// </summary>
- /// <param name="reportedId">The reported identifier.</param>
+ /// <param name="deviceId">The device id.</param>
/// <param name="capabilities">The capabilities.</param>
- void SaveCapabilities(string reportedId, ClientCapabilities capabilities);
+ void SaveCapabilities(string deviceId, ClientCapabilities capabilities);
/// <summary>
/// Gets the capabilities.
/// </summary>
- /// <param name="reportedId">The reported identifier.</param>
+ /// <param name="deviceId">The device id.</param>
/// <returns>ClientCapabilities.</returns>
- ClientCapabilities GetCapabilities(string reportedId);
+ ClientCapabilities GetCapabilities(string deviceId);
/// <summary>
/// Gets the device information.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>DeviceInfo.</returns>
- DeviceInfo GetDevice(string id);
+ Task<DeviceInfo> GetDevice(string id);
+
+ /// <summary>
+ /// Gets devices based on the provided query.
+ /// </summary>
+ /// <param name="query">The device query.</param>
+ /// <returns>A <see cref="Task{QueryResult}"/> representing the retrieval of the devices.</returns>
+ Task<QueryResult<Device>> GetDevices(DeviceQuery query);
+
+ Task<QueryResult<DeviceInfo>> GetDeviceInfos(DeviceQuery query);
/// <summary>
/// Gets the devices.
/// </summary>
- /// <param name="query">The query.</param>
+ /// <param name="userId">The user's id, or <c>null</c>.</param>
+ /// <param name="supportsSync">A value indicating whether the device supports sync, or <c>null</c>.</param>
/// <returns>IEnumerable&lt;DeviceInfo&gt;.</returns>
- QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
+ Task<QueryResult<DeviceInfo>> GetDevicesForUser(Guid? userId, bool? supportsSync);
+
+ Task DeleteDevice(Device device);
/// <summary>
/// Determines whether this instance [can access device] the specified user identifier.
@@ -51,8 +73,8 @@ namespace MediaBrowser.Controller.Devices
/// <returns>Whether the user can access the device.</returns>
bool CanAccessDevice(User user, string deviceId);
- void UpdateDeviceOptions(string deviceId, DeviceOptions options);
+ Task UpdateDeviceOptions(string deviceId, string deviceName);
- DeviceOptions GetDeviceOptions(string deviceId);
+ Task<DeviceOptions> GetDeviceOptions(string deviceId);
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 7bf1219ec..536668e50 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -43,7 +43,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public override bool SupportsPlayedStatus => true;
[JsonIgnore]
- public override bool SupportsPeople => false;
+ public override bool SupportsPeople => true;
[JsonIgnore]
public override bool SupportsAddingToPlaylist => true;
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 067fecd87..1237268d7 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -19,6 +19,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
@@ -43,18 +44,10 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// The trailer folder name.
/// </summary>
- public const string TrailerFolderName = "trailers";
+ public const string TrailersFolderName = "trailers";
public const string ThemeSongsFolderName = "theme-music";
- public const string ThemeSongFilename = "theme";
+ public const string ThemeSongFileName = "theme";
public const string ThemeVideosFolderName = "backdrops";
- public const string ExtrasFolderName = "extras";
- public const string BehindTheScenesFolderName = "behind the scenes";
- public const string DeletedScenesFolderName = "deleted scenes";
- public const string InterviewFolderName = "interviews";
- public const string SceneFolderName = "scenes";
- public const string SampleFolderName = "samples";
- public const string ShortsFolderName = "shorts";
- public const string FeaturettesFolderName = "featurettes";
/// <summary>
/// The supported image extensions.
@@ -92,16 +85,20 @@ namespace MediaBrowser.Controller.Entities
};
public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
- public static readonly string[] AllExtrasTypesFolderNames =
- {
- ExtrasFolderName,
- BehindTheScenesFolderName,
- DeletedScenesFolderName,
- InterviewFolderName,
- SceneFolderName,
- SampleFolderName,
- ShortsFolderName,
- FeaturettesFolderName
+
+ /// <summary>
+ /// The supported extra folder names and types. See <see cref="Emby.Naming.Common.NamingOptions" />.
+ /// </summary>
+ public static readonly Dictionary<string, ExtraType> AllExtrasTypesFolderNames = new Dictionary<string, ExtraType>(StringComparer.OrdinalIgnoreCase)
+ {
+ ["extras"] = MediaBrowser.Model.Entities.ExtraType.Unknown,
+ ["behind the scenes"] = MediaBrowser.Model.Entities.ExtraType.BehindTheScenes,
+ ["deleted scenes"] = MediaBrowser.Model.Entities.ExtraType.DeletedScene,
+ ["interviews"] = MediaBrowser.Model.Entities.ExtraType.Interview,
+ ["scenes"] = MediaBrowser.Model.Entities.ExtraType.Scene,
+ ["samples"] = MediaBrowser.Model.Entities.ExtraType.Sample,
+ ["shorts"] = MediaBrowser.Model.Entities.ExtraType.Clip,
+ ["featurettes"] = MediaBrowser.Model.Entities.ExtraType.Clip
};
private string _sortName;
@@ -112,7 +109,7 @@ namespace MediaBrowser.Controller.Entities
private string _name;
- public static char SlugChar = '-';
+ public const char SlugChar = '-';
protected BaseItem()
{
@@ -1357,7 +1354,7 @@ namespace MediaBrowser.Controller.Entities
// Support plex/xbmc convention
files.AddRange(fileSystemChildren
- .Where(i => !i.IsDirectory && System.IO.Path.GetFileNameWithoutExtension(i.FullName.AsSpan()).Equals(ThemeSongFilename, StringComparison.OrdinalIgnoreCase)));
+ .Where(i => !i.IsDirectory && System.IO.Path.GetFileNameWithoutExtension(i.FullName.AsSpan()).Equals(ThemeSongFileName, StringComparison.OrdinalIgnoreCase)));
return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
.OfType<Audio.Audio>()
@@ -1416,39 +1413,24 @@ namespace MediaBrowser.Controller.Entities
protected virtual BaseItem[] LoadExtras(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
- var extras = new List<Video>();
-
- var libraryOptions = new LibraryOptions();
- var folders = fileSystemChildren.Where(i => i.IsDirectory).ToList();
- foreach (var extraFolderName in AllExtrasTypesFolderNames)
- {
- var files = folders
- .Where(i => string.Equals(i.Name, extraFolderName, StringComparison.OrdinalIgnoreCase))
- .SelectMany(i => FileSystem.GetFiles(i.FullName));
-
- // Re-using the same instance of LibraryOptions since it looks like it's never being altered.
- extras.AddRange(LibraryManager.ResolvePaths(files, directoryService, null, libraryOptions)
+ return fileSystemChildren
+ .Where(child => child.IsDirectory && AllExtrasTypesFolderNames.ContainsKey(child.Name))
+ .SelectMany(folder => LibraryManager
+ .ResolvePaths(FileSystem.GetFiles(folder.FullName), directoryService, null, new LibraryOptions())
.OfType<Video>()
- .Select(item =>
+ .Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
- if (LibraryManager.GetItemById(item.Id) is Video dbItem)
+ if (LibraryManager.GetItemById(video.Id) is Video dbItem)
{
- item = dbItem;
+ video = dbItem;
}
- // Use some hackery to get the extra type based on foldername
- item.ExtraType = Enum.TryParse(extraFolderName.Replace(" ", string.Empty, StringComparison.Ordinal), true, out ExtraType extraType)
- ? extraType
- : Model.Entities.ExtraType.Unknown;
-
- return item;
-
- // Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path));
- }
-
- return extras.ToArray();
+ video.ExtraType = AllExtrasTypesFolderNames[folder.Name];
+ return video;
+ })
+ .OrderBy(video => video.Path)) // Sort them so that the list can be easily compared for changes
+ .ToArray();
}
public Task RefreshMetadata(CancellationToken cancellationToken)
@@ -2050,7 +2032,7 @@ namespace MediaBrowser.Controller.Entities
public virtual string GetClientTypeName()
{
- if (IsFolder && SourceType == SourceType.Channel && !(this is Channel))
+ if (IsFolder && SourceType == SourceType.Channel && this is not Channel)
{
return "ChannelFolderItem";
}
@@ -2439,6 +2421,17 @@ namespace MediaBrowser.Controller.Entities
};
}
+ // Music albums usually don't have dedicated backdrops, so return one from the artist instead
+ if (GetType() == typeof(MusicAlbum) && imageType == ImageType.Backdrop)
+ {
+ var artist = FindParent<MusicArtist>();
+
+ if (artist != null)
+ {
+ return artist.GetImages(imageType).ElementAtOrDefault(imageIndex);
+ }
+ }
+
return GetImages(imageType)
.ElementAtOrDefault(imageIndex);
}
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 0fb4771dd..7dc7f774d 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -97,8 +97,7 @@ namespace MediaBrowser.Controller.Entities
{
try
{
- var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions;
- if (result == null)
+ if (XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) is not LibraryOptions result)
{
return new LibraryOptions();
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index d45a02cf2..18b4ec3c6 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -233,7 +233,7 @@ namespace MediaBrowser.Controller.Entities
public override bool IsVisible(User user)
{
- if (this is ICollectionFolder && !(this is BasePluginFolder))
+ if (this is ICollectionFolder && this is not BasePluginFolder)
{
var blockedMediaFolders = user.GetPreferenceValues<Guid>(PreferenceKind.BlockedMediaFolders);
if (blockedMediaFolders.Length > 0)
@@ -673,7 +673,7 @@ namespace MediaBrowser.Controller.Entities
{
if (LinkedChildren.Length > 0)
{
- if (!(this is ICollectionFolder))
+ if (this is not ICollectionFolder)
{
return GetChildren(user, true).Count;
}
@@ -730,7 +730,7 @@ namespace MediaBrowser.Controller.Entities
return PostFilterAndSort(items, query, true);
}
- if (!(this is UserRootFolder) && !(this is AggregateFolder) && query.ParentId == Guid.Empty)
+ if (this is not UserRootFolder && this is not AggregateFolder && query.ParentId == Guid.Empty)
{
query.Parent = this;
}
@@ -805,7 +805,7 @@ namespace MediaBrowser.Controller.Entities
{
if (LinkedChildren.Length > 0)
{
- if (!(this is ICollectionFolder))
+ if (this is not ICollectionFolder)
{
Logger.LogDebug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name);
return true;
@@ -1015,17 +1015,17 @@ namespace MediaBrowser.Controller.Entities
if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater))
{
- items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
+ items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.InvariantCultureIgnoreCase) < 1);
}
if (!string.IsNullOrEmpty(query.NameStartsWith))
{
- items = items.Where(i => i.SortName.StartsWith(query.NameStartsWith, StringComparison.CurrentCultureIgnoreCase));
+ items = items.Where(i => i.SortName.StartsWith(query.NameStartsWith, StringComparison.InvariantCultureIgnoreCase));
}
if (!string.IsNullOrEmpty(query.NameLessThan))
{
- items = items.Where(i => string.Compare(query.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
+ items = items.Where(i => string.Compare(query.NameLessThan, i.SortName, StringComparison.InvariantCultureIgnoreCase) == 1);
}
// This must be the last filter
@@ -1545,7 +1545,7 @@ namespace MediaBrowser.Controller.Entities
var childOwner = child.GetOwner() ?? child;
- if (childOwner != null && !(child is IItemByName))
+ if (child is not IItemByName)
{
var childProtocol = childOwner.PathProtocol;
if (!childProtocol.HasValue || childProtocol.Value != Model.MediaInfo.MediaProtocol.File)
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index beda504b9..e4933e968 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -296,7 +296,7 @@ namespace MediaBrowser.Controller.Entities.TV
// Refresh seasons
foreach (var item in items)
{
- if (!(item is Season))
+ if (item is not Season)
{
continue;
}
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index f3bf4749d..e547db523 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -24,6 +24,14 @@ namespace MediaBrowser.Controller.Entities
private readonly object _childIdsLock = new object();
private List<Guid> _childrenIds = null;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserRootFolder"/> class.
+ /// </summary>
+ public UserRootFolder()
+ {
+ IsRoot = true;
+ }
+
[JsonIgnore]
public override bool SupportsInheritedParentImages => false;
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 753c18bc7..3da0a5875 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -2,7 +2,6 @@
#pragma warning disable CS1591
-using System;
using System.Collections.Generic;
using System.Net;
using MediaBrowser.Common;
@@ -104,13 +103,6 @@ namespace MediaBrowser.Controller
/// <returns>The API URL.</returns>
string GetLocalApiUrl(string hostname, string scheme = null, int? port = null);
- /// <summary>
- /// Open a URL in an external browser window.
- /// </summary>
- /// <param name="url">The URL to open.</param>
- /// <exception cref="NotSupportedException"><see cref="CanLaunchWebBrowser"/> is false.</exception>
- void LaunchUrl(string url);
-
IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo();
string ExpandVirtualPath(string path);
diff --git a/MediaBrowser.Controller/Library/IDirectStreamProvider.cs b/MediaBrowser.Controller/Library/IDirectStreamProvider.cs
new file mode 100644
index 000000000..96f8b7eba
--- /dev/null
+++ b/MediaBrowser.Controller/Library/IDirectStreamProvider.cs
@@ -0,0 +1,19 @@
+using System.IO;
+
+namespace MediaBrowser.Controller.Library
+{
+ /// <summary>
+ /// The direct live TV stream provider.
+ /// </summary>
+ /// <remarks>
+ /// Deprecated.
+ /// </remarks>
+ public interface IDirectStreamProvider
+ {
+ /// <summary>
+ /// Gets the live stream, shared streams seek to the end of the file first.
+ /// </summary>
+ /// <returns>The stream.</returns>
+ Stream GetStream();
+ }
+}
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 604960d8b..d40e56c7d 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -595,11 +595,11 @@ namespace MediaBrowser.Controller.Library
Task RemoveVirtualFolder(string name, bool refreshLibrary);
- void AddMediaPath(string virtualFolderName, MediaPathInfo path);
+ void AddMediaPath(string virtualFolderName, MediaPathInfo mediaPath);
- void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
+ void UpdateMediaPath(string virtualFolderName, MediaPathInfo mediaPath);
- void RemoveMediaPath(string virtualFolderName, string path);
+ void RemoveMediaPath(string virtualFolderName, string mediaPath);
QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
diff --git a/MediaBrowser.Controller/Library/ILiveStream.cs b/MediaBrowser.Controller/Library/ILiveStream.cs
index 323aa4876..4c44a17fd 100644
--- a/MediaBrowser.Controller/Library/ILiveStream.cs
+++ b/MediaBrowser.Controller/Library/ILiveStream.cs
@@ -2,6 +2,7 @@
#pragma warning disable CA1711, CS1591
+using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
@@ -25,5 +26,7 @@ namespace MediaBrowser.Controller.Library
Task Open(CancellationToken openCancellationToken);
Task Close();
+
+ Stream GetStream();
}
}
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index fd3631da9..e802796d3 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
@@ -111,6 +110,20 @@ namespace MediaBrowser.Controller.Library
Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
/// <summary>
+ /// Gets the live stream info.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <returns>An instance of <see cref="ILiveStream"/>.</returns>
+ public ILiveStream GetLiveStreamInfo(string id);
+
+ /// <summary>
+ /// Gets the live stream info using the stream's unique id.
+ /// </summary>
+ /// <param name="uniqueId">The unique identifier.</param>
+ /// <returns>An instance of <see cref="ILiveStream"/>.</returns>
+ public ILiveStream GetLiveStreamInfoByUniqueId(string uniqueId);
+
+ /// <summary>
/// Closes the media source.
/// </summary>
/// <param name="id">The live stream identifier.</param>
@@ -126,14 +139,5 @@ namespace MediaBrowser.Controller.Library
void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user);
Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, string cacheKey, bool addProbeDelay, bool isLiveStream, CancellationToken cancellationToken);
-
- Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken);
- }
-
- public interface IDirectStreamProvider
- {
- Task CopyToAsync(Stream stream, CancellationToken cancellationToken);
-
- string GetFilePath();
}
}
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
index cf35b48db..034c40591 100644
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ b/MediaBrowser.Controller/Library/IUserDataManager.cs
@@ -47,7 +47,7 @@ namespace MediaBrowser.Controller.Library
/// <returns>User data dto.</returns>
UserItemDataDto GetUserDataDto(BaseItem item, User user);
- UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions dto_options);
+ UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions options);
/// <summary>
/// Get all user data for the given user.
@@ -69,8 +69,8 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="item">Item to update.</param>
/// <param name="data">Data to update.</param>
- /// <param name="positionTicks">New playstate.</param>
+ /// <param name="reportedPositionTicks">New playstate.</param>
/// <returns>True if playstate was updated.</returns>
- bool UpdatePlayState(BaseItem item, UserItemData data, long? positionTicks);
+ bool UpdatePlayState(BaseItem item, UserItemData data, long? reportedPositionTicks);
}
}
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index 21776f891..993e3e18f 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -72,14 +72,6 @@ namespace MediaBrowser.Controller.Library
/// <param name="user">The user.</param>
/// <exception cref="ArgumentNullException">If user is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If the provided user doesn't exist.</exception>
- void UpdateUser(User user);
-
- /// <summary>
- /// Updates the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <exception cref="ArgumentNullException">If user is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">If the provided user doesn't exist.</exception>
/// <returns>A task representing the update of the user.</returns>
Task UpdateUserAsync(User user);
@@ -110,7 +102,8 @@ namespace MediaBrowser.Controller.Library
/// Resets the easy password.
/// </summary>
/// <param name="user">The user.</param>
- void ResetEasyPassword(User user);
+ /// <returns>Task.</returns>
+ Task ResetEasyPassword(User user);
/// <summary>
/// Changes the password.
@@ -126,7 +119,8 @@ namespace MediaBrowser.Controller.Library
/// <param name="user">The user.</param>
/// <param name="newPassword">New password to use.</param>
/// <param name="newPasswordSha1">Hash of new password.</param>
- void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1);
+ /// <returns>Task.</returns>
+ Task ChangeEasyPassword(User user, string newPassword, string newPasswordSha1);
/// <summary>
/// Gets the user dto.
@@ -169,7 +163,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// This method updates the user's configuration.
/// This is only included as a stopgap until the new API, using this internally is not recommended.
- /// Instead, modify the user object directly, then call <see cref="UpdateUser"/>.
+ /// Instead, modify the user object directly, then call <see cref="UpdateUserAsync"/>.
/// </summary>
/// <param name="userId">The user's Id.</param>
/// <param name="config">The request containing the new user configuration.</param>
@@ -179,7 +173,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// This method updates the user's policy.
/// This is only included as a stopgap until the new API, using this internally is not recommended.
- /// Instead, modify the user object directly, then call <see cref="UpdateUser"/>.
+ /// Instead, modify the user object directly, then call <see cref="UpdateUserAsync"/>.
/// </summary>
/// <param name="userId">The user's Id.</param>
/// <param name="policy">The request containing the new user policy.</param>
diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs
index a49dcacc1..d2ed3465a 100644
--- a/MediaBrowser.Controller/Library/NameExtensions.cs
+++ b/MediaBrowser.Controller/Library/NameExtensions.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Diacritics.Extensions;
-using MediaBrowser.Controller.Extensions;
namespace MediaBrowser.Controller.Library
{
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index bd097c90a..dbd18165d 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -274,7 +274,7 @@ namespace MediaBrowser.Controller.LiveTv
Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber);
- TunerChannelMapping GetTunerChannelMapping(ChannelInfo channel, NameValuePair[] mappings, List<ChannelInfo> providerChannels);
+ TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, NameValuePair[] mappings, List<ChannelInfo> providerChannels);
/// <summary>
/// Gets the lineups.
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
index 897f263f3..ce34954e3 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
@@ -70,10 +70,10 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary>
/// Updates the timer asynchronous.
/// </summary>
- /// <param name="info">The information.</param>
+ /// <param name="updatedTimer">The updated timer information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken);
+ Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken);
/// <summary>
/// Updates the series timer asynchronous.
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 141bb91c5..bdb379332 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.MediaEncoding
"ConstrainedHigh"
};
+ private static readonly Version _minVersionForCudaOverlay = new Version(4, 4);
+
public EncodingHelper(
IMediaEncoder mediaEncoder,
ISubtitleEncoder subtitleEncoder)
@@ -106,17 +108,41 @@ namespace MediaBrowser.Controller.MediaEncoding
private bool IsCudaSupported()
{
return _mediaEncoder.SupportsHwaccel("cuda")
- && _mediaEncoder.SupportsFilter("scale_cuda", null)
- && _mediaEncoder.SupportsFilter("yadif_cuda", null);
+ && _mediaEncoder.SupportsFilter("scale_cuda")
+ && _mediaEncoder.SupportsFilter("yadif_cuda")
+ && _mediaEncoder.SupportsFilter("hwupload_cuda");
}
- private bool IsTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
+ private bool IsOpenclTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
{
var videoStream = state.VideoStream;
- return IsColorDepth10(state)
+ if (videoStream == null)
+ {
+ return false;
+ }
+
+ return options.EnableTonemapping
+ && (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ && IsColorDepth10(state)
&& _mediaEncoder.SupportsHwaccel("opencl")
- && options.EnableTonemapping
- && string.Equals(videoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase);
+ && _mediaEncoder.SupportsFilter("tonemap_opencl");
+ }
+
+ private bool IsCudaTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
+ {
+ var videoStream = state.VideoStream;
+ if (videoStream == null)
+ {
+ return false;
+ }
+
+ return options.EnableTonemapping
+ && (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ && IsColorDepth10(state)
+ && _mediaEncoder.SupportsHwaccel("cuda")
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapCudaName);
}
private bool IsVppTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
@@ -132,23 +158,25 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
- return IsColorDepth10(state)
+ return options.EnableVppTonemapping
+ && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ && IsColorDepth10(state)
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
&& _mediaEncoder.SupportsHwaccel("vaapi")
- && options.EnableVppTonemapping
- && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase);
+ && _mediaEncoder.SupportsFilter("tonemap_vaapi");
}
// Hybrid VPP tonemapping for QSV with VAAPI
if (OperatingSystem.IsLinux() && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
- return IsColorDepth10(state)
+ return options.EnableVppTonemapping
+ && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
+ && IsColorDepth10(state)
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
&& _mediaEncoder.SupportsHwaccel("vaapi")
- && _mediaEncoder.SupportsHwaccel("qsv")
- && options.EnableVppTonemapping
- && string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase);
+ && _mediaEncoder.SupportsFilter("tonemap_vaapi")
+ && _mediaEncoder.SupportsHwaccel("qsv");
}
// Native VPP tonemapping may come to QSV in the future.
@@ -178,11 +206,17 @@ namespace MediaBrowser.Controller.MediaEncoding
return GetH264Encoder(state, encodingOptions);
}
- if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(codec, "vp8", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
{
return "libvpx";
}
+ if (string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
+ {
+ return "libvpx-vp9";
+ }
+
if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase))
{
return "wmv2";
@@ -414,7 +448,8 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
{
- return "vpx";
+ // TODO: this may not always mean VP8, as the codec ages
+ return "vp8";
}
if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
@@ -497,14 +532,19 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Gets the input argument.
/// </summary>
/// <param name="state">Encoding state.</param>
- /// <param name="encodingOptions">Encoding options.</param>
+ /// <param name="options">Encoding options.</param>
/// <returns>Input arguments.</returns>
- public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
+ public string GetInputArgument(EncodingJobInfo state, EncodingOptions options)
{
var arg = new StringBuilder();
- var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
- var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
+ var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
+ var outputVideoCodec = GetVideoEncoder(state, options) ?? string.Empty;
+ var isWindows = OperatingSystem.IsWindows();
+ var isLinux = OperatingSystem.IsLinux();
+ var isMacOS = OperatingSystem.IsMacOS();
+#pragma warning disable CA1508 // Defaults to string.Empty
var isSwDecoder = string.IsNullOrEmpty(videoDecoder);
+#pragma warning restore CA1508
var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
@@ -512,42 +552,40 @@ namespace MediaBrowser.Controller.MediaEncoding
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
var isNvdecDecoder = videoDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase);
var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase);
- var isWindows = OperatingSystem.IsWindows();
- var isLinux = OperatingSystem.IsLinux();
- var isMacOS = OperatingSystem.IsMacOS();
- var isTonemappingSupported = IsTonemappingSupported(state, encodingOptions);
- var isVppTonemappingSupported = IsVppTonemappingSupported(state, encodingOptions);
+ var isCuvidVp9Decoder = videoDecoder.Contains("vp9_cuvid", StringComparison.OrdinalIgnoreCase);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
+ var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
if (!IsCopyCodec(outputVideoCodec))
{
if (state.IsVideoRequest
&& _mediaEncoder.SupportsHwaccel("vaapi")
- && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
+ && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
if (isVaapiDecoder)
{
- if (isTonemappingSupported && !isVppTonemappingSupported)
+ if (isOpenclTonemappingSupported && !isVppTonemappingSupported)
{
- arg.Append("-init_hw_device vaapi=va:")
- .Append(encodingOptions.VaapiDevice)
- .Append(' ')
- .Append("-init_hw_device opencl=ocl@va ")
- .Append("-hwaccel_device va ")
- .Append("-hwaccel_output_format vaapi ")
- .Append("-filter_hw_device ocl ");
+ arg.Append("-init_hw_device vaapi=va:")
+ .Append(options.VaapiDevice)
+ .Append(" -init_hw_device opencl=ocl@va ")
+ .Append("-hwaccel_device va ")
+ .Append("-hwaccel_output_format vaapi ")
+ .Append("-filter_hw_device ocl ");
}
else
{
arg.Append("-hwaccel_output_format vaapi ")
.Append("-vaapi_device ")
- .Append(encodingOptions.VaapiDevice)
+ .Append(options.VaapiDevice)
.Append(' ');
}
}
else if (!isVaapiDecoder && isVaapiEncoder)
{
arg.Append("-vaapi_device ")
- .Append(encodingOptions.VaapiDevice)
+ .Append(options.VaapiDevice)
.Append(' ');
}
@@ -555,7 +593,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
- && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+ && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -591,9 +629,8 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (isVaapiDecoder && isVppTonemappingSupported)
{
arg.Append("-init_hw_device vaapi=va:")
- .Append(encodingOptions.VaapiDevice)
- .Append(' ')
- .Append("-init_hw_device qsv@va ")
+ .Append(options.VaapiDevice)
+ .Append(" -init_hw_device qsv@va ")
.Append("-hwaccel_output_format vaapi ");
}
@@ -602,7 +639,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
- && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
+ && string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
&& isNvdecDecoder)
{
// Fix for 'No decoder surfaces left' error. https://trac.ffmpeg.org/ticket/7562
@@ -610,22 +647,31 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (state.IsVideoRequest
- && ((string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
- && (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder))
- || (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)
- && (isD3d11vaDecoder || isSwDecoder))))
+ && string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
+ && (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder))
+ {
+ if (!isCudaTonemappingSupported && isOpenclTonemappingSupported)
+ {
+ arg.Append("-init_hw_device opencl=ocl:")
+ .Append(options.OpenclDevice)
+ .Append(" -filter_hw_device ocl ");
+ }
+ }
+
+ if (state.IsVideoRequest
+ && string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)
+ && (isD3d11vaDecoder || isSwDecoder))
{
- if (isTonemappingSupported)
+ if (isOpenclTonemappingSupported)
{
arg.Append("-init_hw_device opencl=ocl:")
- .Append(encodingOptions.OpenclDevice)
- .Append(' ')
- .Append("-filter_hw_device ocl ");
+ .Append(options.OpenclDevice)
+ .Append(" -filter_hw_device ocl ");
}
}
if (state.IsVideoRequest
- && string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ && string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
{
arg.Append("-hwaccel videotoolbox ");
}
@@ -735,49 +781,37 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetVideoBitrateParam(EncodingJobInfo state, string videoCodec)
{
- var bitrate = state.OutputVideoBitrate;
-
- if (bitrate.HasValue)
+ if (state.OutputVideoBitrate == null)
{
- if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
- {
- // When crf is used with vpx, b:v becomes a max rate
- // https://trac.ffmpeg.org/wiki/Encode/VP9
- return string.Format(
- CultureInfo.InvariantCulture,
- " -maxrate:v {0} -bufsize:v {1} -b:v {0}",
- bitrate.Value,
- bitrate.Value * 2);
- }
+ return string.Empty;
+ }
- if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
- {
- return string.Format(
- CultureInfo.InvariantCulture,
- " -b:v {0}",
- bitrate.Value);
- }
+ int bitrate = state.OutputVideoBitrate.Value;
- if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
- {
- // h264
- return string.Format(
- CultureInfo.InvariantCulture,
- " -maxrate {0} -bufsize {1}",
- bitrate.Value,
- bitrate.Value * 2);
- }
+ // Currently use the same buffer size for all encoders
+ int bufsize = bitrate * 2;
- // h264
- return string.Format(
- CultureInfo.InvariantCulture,
- " -b:v {0} -maxrate {0} -bufsize {1}",
- bitrate.Value,
- bitrate.Value * 2);
+ if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoCodec, "libvpx-vp9", StringComparison.OrdinalIgnoreCase))
+ {
+ // When crf is used with vpx, b:v becomes a max rate
+ // https://trac.ffmpeg.org/wiki/Encode/VP8
+ // https://trac.ffmpeg.org/wiki/Encode/VP9
+ return FormattableString.Invariant($" -maxrate:v {bitrate} -bufsize:v {bufsize} -b:v {bitrate}");
}
- return string.Empty;
+ if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase))
+ {
+ return FormattableString.Invariant($" -b:v {bitrate}");
+ }
+
+ if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
+ {
+ return FormattableString.Invariant($" -maxrate {bitrate} -bufsize {bufsize}");
+ }
+
+ return FormattableString.Invariant($" -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
}
public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level)
@@ -1160,7 +1194,7 @@ namespace MediaBrowser.Controller.MediaEncoding
param += " -header_insertion_mode gop -gops_per_idr 1";
}
}
- else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm
+ else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // vp8
{
// Values 0-3, 0 being highest quality but slower
var profileScore = 0;
@@ -1188,6 +1222,55 @@ namespace MediaBrowser.Controller.MediaEncoding
qmin,
qmax);
}
+ else if (string.Equals(videoEncoder, "libvpx-vp9", StringComparison.OrdinalIgnoreCase)) // vp9
+ {
+ // When `-deadline` is set to `good` or `best`, `-cpu-used` ranges from 0-5.
+ // When `-deadline` is set to `realtime`, `-cpu-used` ranges from 0-15.
+ // Resources:
+ // * https://trac.ffmpeg.org/wiki/Encode/VP9
+ // * https://superuser.com/questions/1586934
+ // * https://developers.google.com/media/vp9
+ param += encodingOptions.EncoderPreset switch
+ {
+ "veryslow" => " -deadline best -cpu-used 0",
+ "slower" => " -deadline best -cpu-used 2",
+ "slow" => " -deadline best -cpu-used 3",
+ "medium" => " -deadline good -cpu-used 0",
+ "fast" => " -deadline good -cpu-used 1",
+ "faster" => " -deadline good -cpu-used 2",
+ "veryfast" => " -deadline good -cpu-used 3",
+ "superfast" => " -deadline good -cpu-used 4",
+ "ultrafast" => " -deadline good -cpu-used 5",
+ _ => " -deadline good -cpu-used 1"
+ };
+
+ // TODO: until VP9 gets its own CRF setting, base CRF on H.265.
+ int h265Crf = encodingOptions.H265Crf;
+ int defaultVp9Crf = 31;
+ if (h265Crf >= 0 && h265Crf <= 51)
+ {
+ // This conversion factor is chosen to match the default CRF for H.265 to the
+ // recommended 1080p CRF from Google. The factor also maps the logarithmic CRF
+ // scale of x265 [0, 51] to that of VP9 [0, 63] relatively well.
+
+ // Resources:
+ // * https://developers.google.com/media/vp9/settings/vod
+ const float H265ToVp9CrfConversionFactor = 1.12F;
+
+ var vp9Crf = Convert.ToInt32(h265Crf * H265ToVp9CrfConversionFactor);
+
+ // Encoder allows for CRF values in the range [0, 63].
+ vp9Crf = Math.Clamp(vp9Crf, 0, 63);
+
+ param += FormattableString.Invariant($" -crf {vp9Crf}");
+ }
+ else
+ {
+ param += FormattableString.Invariant($" -crf {defaultVp9Crf}");
+ }
+
+ param += " -row-mt 1 -profile 1";
+ }
else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase))
{
param += " -mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2";
@@ -1759,7 +1842,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var request = state.BaseRequest;
- var inputChannels = audioStream?.Channels;
+ var inputChannels = audioStream.Channels;
if (inputChannels <= 0)
{
@@ -2010,14 +2093,18 @@ namespace MediaBrowser.Controller.MediaEncoding
var isQsvHevcEncoder = outputVideoCodec.Contains("hevc_qsv", StringComparison.OrdinalIgnoreCase);
var isNvdecDecoder = videoDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase);
var isNvencEncoder = outputVideoCodec.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
- var isTonemappingSupported = IsTonemappingSupported(state, options);
- var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
+
+ var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
+ var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= _minVersionForCudaOverlay;
+ var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
// Tonemapping and burn-in graphical subtitles requires overlay_vaapi.
// But it's still in ffmpeg mailing list. Disable it for now.
- if (isTonemappingSupportedOnVaapi && isTonemappingSupported && !isVppTonemappingSupported)
+ if (isTonemappingSupportedOnVaapi && isOpenclTonemappingSupported && !isVppTonemappingSupported)
{
return GetOutputSizeParam(state, options, outputVideoCodec);
}
@@ -2027,8 +2114,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// Adjust the size of graphical subtitles to fit the video stream.
var videoStream = state.VideoStream;
- var inputWidth = videoStream?.Width;
- var inputHeight = videoStream?.Height;
+ var inputWidth = videoStream.Width;
+ var inputHeight = videoStream.Height;
var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight);
if (width.HasValue && height.HasValue)
@@ -2043,13 +2130,22 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(videoSizeParam)
&& !(isTonemappingSupportedOnQsv && isVppTonemappingSupported))
{
- // For QSV, feed it into hardware encoder now
+ // upload graphical subtitle to QSV
if (isLinux && (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)))
{
videoSizeParam += ",hwupload=extra_hw_frames=64";
}
}
+
+ if (!string.IsNullOrEmpty(videoSizeParam))
+ {
+ // upload graphical subtitle to cuda
+ if (isNvdecDecoder && isNvencEncoder && isCudaOverlaySupported && isCudaFormatConversionSupported)
+ {
+ videoSizeParam += ",hwupload_cuda";
+ }
+ }
}
var mapPrefix = state.SubtitleStream.IsExternal ?
@@ -2062,9 +2158,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
// Always put the scaler before the overlay for better performance
- var retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
+ var retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
// When the input may or may not be hardware VAAPI decodable
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
@@ -2075,9 +2171,9 @@ namespace MediaBrowser.Controller.MediaEncoding
[sub]: SW scaling subtitle to FixedOutputSize
[base][sub]: SW overlay
*/
- retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
}
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
@@ -2090,9 +2186,9 @@ namespace MediaBrowser.Controller.MediaEncoding
[sub]: SW scaling subtitle to FixedOutputSize
[base][sub]: SW overlay
*/
- retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
}
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase))
@@ -2109,16 +2205,25 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isLinux)
{
- retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"";
}
}
else if (isNvdecDecoder && isNvencEncoder)
{
- retStr = !outputSizeParam.IsEmpty
- ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay,format=nv12|yuv420p,hwupload_cuda\""
- : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay,format=nv12|yuv420p,hwupload_cuda\"";
+ if (isCudaOverlaySupported && isCudaFormatConversionSupported)
+ {
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]scale_cuda=format=yuv420p[base];[base][sub]overlay_cuda\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_cuda\"";
+ }
+ else
+ {
+ retStr = outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay,format=nv12|yuv420p,hwupload_cuda\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay,format=nv12|yuv420p,hwupload_cuda\"";
+ }
}
return string.Format(
@@ -2215,11 +2320,11 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiHevcEncoder = videoEncoder.Contains("hevc_vaapi", StringComparison.OrdinalIgnoreCase);
var isQsvH264Encoder = videoEncoder.Contains("h264_qsv", StringComparison.OrdinalIgnoreCase);
var isQsvHevcEncoder = videoEncoder.Contains("hevc_qsv", StringComparison.OrdinalIgnoreCase);
- var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
- var isP010PixFmtRequired = (isTonemappingSupportedOnVaapi && (isTonemappingSupported || isVppTonemappingSupported))
+ var isP010PixFmtRequired = (isTonemappingSupportedOnVaapi && (isOpenclTonemappingSupported || isVppTonemappingSupported))
|| (isTonemappingSupportedOnQsv && isVppTonemappingSupported);
var outputPixFmt = "format=nv12";
@@ -2270,15 +2375,23 @@ namespace MediaBrowser.Controller.MediaEncoding
var outputWidth = width.Value;
var outputHeight = height.Value;
- var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isNvencEncoder = videoEncoder.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase);
- var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilter("scale_cuda", "Output format (default \"same\")");
+ var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
+ var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= _minVersionForCudaOverlay;
+ var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
+ var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var outputPixFmt = string.Empty;
if (isCudaFormatConversionSupported)
{
- outputPixFmt = "format=nv12";
- if (isTonemappingSupported && isTonemappingSupportedOnNvenc)
+ outputPixFmt = (hasGraphicalSubs && isCudaOverlaySupported && isNvencEncoder)
+ ? "format=yuv420p"
+ : "format=nv12";
+ if ((isOpenclTonemappingSupported || isCudaTonemappingSupported)
+ && isTonemappingSupportedOnNvenc)
{
outputPixFmt = "format=p010";
}
@@ -2556,16 +2669,21 @@ namespace MediaBrowser.Controller.MediaEncoding
var isNvencEncoder = outputVideoCodec.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
var isCuvidH264Decoder = videoDecoder.Contains("h264_cuvid", StringComparison.OrdinalIgnoreCase);
var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase);
+ var isCuvidVp9Decoder = videoDecoder.Contains("vp9_cuvid", StringComparison.OrdinalIgnoreCase);
var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1;
var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1;
var isLinux = OperatingSystem.IsLinux();
var isColorDepth10 = IsColorDepth10(state);
- var isTonemappingSupported = IsTonemappingSupported(state, options);
- var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
- var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder);
+
+ var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder);
var isTonemappingSupportedOnAmf = string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && (isD3d11vaDecoder || isSwDecoder);
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
+ var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
+ var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
+ var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
+ var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
+ var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= _minVersionForCudaOverlay;
var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -2577,19 +2695,25 @@ namespace MediaBrowser.Controller.MediaEncoding
var isScalingInAdvance = false;
var isCudaDeintInAdvance = false;
var isHwuploadCudaRequired = false;
+ var isNoTonemapFilterApplied = true;
var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
// Add OpenCL tonemapping filter for NVENC/AMF/VAAPI.
- if (isTonemappingSupportedOnNvenc || isTonemappingSupportedOnAmf || (isTonemappingSupportedOnVaapi && !isVppTonemappingSupported))
+ if ((isTonemappingSupportedOnNvenc && !isCudaTonemappingSupported) || isTonemappingSupportedOnAmf || (isTonemappingSupportedOnVaapi && !isVppTonemappingSupported))
{
- // Currently only with the use of NVENC decoder can we get a decent performance.
- // Currently only the HEVC/H265 format is supported with NVDEC decoder.
// NVIDIA Pascal and Turing or higher are recommended.
// AMD Polaris and Vega or higher are recommended.
// Intel Kaby Lake or newer is required.
- if (isTonemappingSupported)
+ if (isOpenclTonemappingSupported)
{
+ isNoTonemapFilterApplied = false;
+ var inputHdrParams = GetInputHdrParams(videoStream.ColorTransfer);
+ if (!string.IsNullOrEmpty(inputHdrParams))
+ {
+ filters.Add(inputHdrParams);
+ }
+
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
if (options.TonemappingParam != 0)
@@ -2661,7 +2785,11 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwdownload,format=p010");
}
- if (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder || isD3d11vaDecoder)
+ if (isNvdecDecoder
+ || isCuvidHevcDecoder
+ || isCuvidVp9Decoder
+ || isSwDecoder
+ || isD3d11vaDecoder)
{
// Upload the HDR10 or HLG data to the OpenCL device,
// use tonemap_opencl filter for tone mapping,
@@ -2669,6 +2797,14 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwupload");
}
+ // Fallback to hable if bt2390 is chosen but not supported in tonemap_opencl.
+ var isBt2390SupportedInOpenclTonemap = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapOpenclBt2390);
+ if (string.Equals(options.TonemappingAlgorithm, "bt2390", StringComparison.OrdinalIgnoreCase)
+ && !isBt2390SupportedInOpenclTonemap)
+ {
+ options.TonemappingAlgorithm = "hable";
+ }
+
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
@@ -2680,7 +2816,11 @@ namespace MediaBrowser.Controller.MediaEncoding
options.TonemappingParam,
options.TonemappingRange));
- if (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder || isD3d11vaDecoder)
+ if (isNvdecDecoder
+ || isCuvidHevcDecoder
+ || isCuvidVp9Decoder
+ || isSwDecoder
+ || isD3d11vaDecoder)
{
filters.Add("hwdownload");
filters.Add("format=nv12");
@@ -2696,12 +2836,18 @@ namespace MediaBrowser.Controller.MediaEncoding
// Reverse the data route from opencl to vaapi.
filters.Add("hwmap=derive_device=vaapi:reverse=1");
}
+
+ var outputSdrParams = GetOutputSdrParams(options.TonemappingRange);
+ if (!string.IsNullOrEmpty(outputSdrParams))
+ {
+ filters.Add(outputSdrParams);
+ }
}
}
// When the input may or may not be hardware VAAPI decodable.
if ((isVaapiH264Encoder || isVaapiHevcEncoder)
- && !(isTonemappingSupportedOnVaapi && (isTonemappingSupported || isVppTonemappingSupported)))
+ && !(isTonemappingSupportedOnVaapi && (isOpenclTonemappingSupported || isVppTonemappingSupported)))
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
@@ -2809,6 +2955,61 @@ namespace MediaBrowser.Controller.MediaEncoding
request.MaxHeight));
}
+ // Add Cuda tonemapping filter.
+ if (isNvdecDecoder && isCudaTonemappingSupported)
+ {
+ isNoTonemapFilterApplied = false;
+ var inputHdrParams = GetInputHdrParams(videoStream.ColorTransfer);
+ if (!string.IsNullOrEmpty(inputHdrParams))
+ {
+ filters.Add(inputHdrParams);
+ }
+
+ var parameters = (hasGraphicalSubs && isCudaOverlaySupported && isNvencEncoder)
+ ? "tonemap_cuda=format=yuv420p:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:peak={1}:desat={2}"
+ : "tonemap_cuda=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:peak={1}:desat={2}";
+
+ if (options.TonemappingParam != 0)
+ {
+ parameters += ":param={3}";
+ }
+
+ if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
+ {
+ parameters += ":range={4}";
+ }
+
+ filters.Add(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ parameters,
+ options.TonemappingAlgorithm,
+ options.TonemappingPeak,
+ options.TonemappingDesat,
+ options.TonemappingParam,
+ options.TonemappingRange));
+
+ if (isLibX264Encoder
+ || isLibX265Encoder
+ || hasTextSubs
+ || (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder))
+ {
+ if (isNvencEncoder)
+ {
+ isHwuploadCudaRequired = true;
+ }
+
+ filters.Add("hwdownload");
+ filters.Add("format=nv12");
+ }
+
+ var outputSdrParams = GetOutputSdrParams(options.TonemappingRange);
+ if (!string.IsNullOrEmpty(outputSdrParams))
+ {
+ filters.Add(outputSdrParams);
+ }
+ }
+
// Add VPP tonemapping filter for VAAPI.
// Full hardware based video post processing, faster than OpenCL but lacks fine tuning options.
if ((isTonemappingSupportedOnVaapi || isTonemappingSupportedOnQsv)
@@ -2818,10 +3019,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Another case is when using Nvenc decoder.
- if (isNvdecDecoder && !isTonemappingSupported)
+ if (isNvdecDecoder && !isOpenclTonemappingSupported && !isCudaTonemappingSupported)
{
var codec = videoStream.Codec;
- var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilter("scale_cuda", "Output format (default \"same\")");
+ var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
// Assert 10-bit hardware decodable
if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
@@ -2830,7 +3031,10 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isCudaFormatConversionSupported)
{
- if (isLibX264Encoder || isLibX265Encoder || hasSubs)
+ if (isLibX264Encoder
+ || isLibX265Encoder
+ || hasTextSubs
+ || (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder))
{
if (isNvencEncoder)
{
@@ -2857,7 +3061,11 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Assert 8-bit hardware decodable
- else if (!isColorDepth10 && (isLibX264Encoder || isLibX265Encoder || hasSubs))
+ else if (!isColorDepth10
+ && (isLibX264Encoder
+ || isLibX265Encoder
+ || hasTextSubs
+ || (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder)))
{
if (isNvencEncoder)
{
@@ -2878,7 +3086,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// Convert hw context from ocl to va.
// For tonemapping and text subs burn-in.
- if (isTonemappingSupportedOnVaapi && isTonemappingSupported && !isVppTonemappingSupported)
+ if (isTonemappingSupportedOnVaapi && isOpenclTonemappingSupported && !isVppTonemappingSupported)
{
filters.Add("scale_vaapi");
}
@@ -2924,6 +3132,17 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("hwupload_cuda");
}
+ // If no tonemap filter is applied,
+ // tag the video range as SDR to prevent the encoder from encoding HDR video.
+ if (isNoTonemapFilterApplied)
+ {
+ var outputSdrParams = GetOutputSdrParams(null);
+ if (!string.IsNullOrEmpty(outputSdrParams))
+ {
+ filters.Add(outputSdrParams);
+ }
+ }
+
var output = string.Empty;
if (filters.Count > 0)
{
@@ -2936,6 +3155,36 @@ namespace MediaBrowser.Controller.MediaEncoding
return output;
}
+ public static string GetInputHdrParams(string colorTransfer)
+ {
+ if (string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ {
+ // HLG
+ return "setparams=color_primaries=bt2020:color_trc=arib-std-b67:colorspace=bt2020nc";
+ }
+ else
+ {
+ // HDR10
+ return "setparams=color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc";
+ }
+ }
+
+ public static string GetOutputSdrParams(string tonemappingRange)
+ {
+ // SDR
+ if (string.Equals(tonemappingRange, "tv", StringComparison.OrdinalIgnoreCase))
+ {
+ return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=tv";
+ }
+
+ if (string.Equals(tonemappingRange, "pc", StringComparison.OrdinalIgnoreCase))
+ {
+ return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=pc";
+ }
+
+ return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709";
+ }
+
/// <summary>
/// Gets the number of threads.
/// </summary>
@@ -2946,20 +3195,16 @@ namespace MediaBrowser.Controller.MediaEncoding
#nullable enable
public static int GetNumberOfThreads(EncodingJobInfo? state, EncodingOptions encodingOptions, string? outputVideoCodec)
{
- if (string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
- {
- // per docs:
- // -threads number of threads to use for encoding, can't be 0 [auto] with VP8
- // (recommended value : number of real cores - 1)
- return Math.Max(Environment.ProcessorCount - 1, 1);
- }
+ // VP8 and VP9 encoders must have their thread counts set.
+ bool mustSetThreadCount = string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(outputVideoCodec, "libvpx-vp9", StringComparison.OrdinalIgnoreCase);
var threads = state?.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount;
- // Automatic
if (threads <= 0)
{
- return 0;
+ // Automatically set thread count
+ return mustSetThreadCount ? Math.Max(Environment.ProcessorCount - 1, 1) : 0;
}
else if (threads >= Environment.ProcessorCount)
{
@@ -3101,7 +3346,7 @@ namespace MediaBrowser.Controller.MediaEncoding
inputModifier += " " + videoDecoder;
if (!IsCopyCodec(state.OutputVideoCodec)
- && (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1)
+ && videoDecoder.Contains("cuvid", StringComparison.OrdinalIgnoreCase))
{
var videoStream = state.VideoStream;
var inputWidth = videoStream?.Width;
@@ -3110,7 +3355,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight);
- if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1
+ if (videoDecoder.Contains("cuvid", StringComparison.OrdinalIgnoreCase)
&& width.HasValue
&& height.HasValue)
{
@@ -3406,8 +3651,13 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)
&& IsVppTonemappingSupported(state, encodingOptions))
{
- // Since tonemap_vaapi only support HEVC for now, no need to check the codec again.
- return GetHwaccelType(state, encodingOptions, "hevc", isColorDepth10);
+ var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
+ var isQsvEncoder = outputVideoCodec.Contains("qsv", StringComparison.OrdinalIgnoreCase);
+ if (isQsvEncoder)
+ {
+ // Since tonemap_vaapi only support HEVC for now, no need to check the codec again.
+ return GetHwaccelType(state, encodingOptions, "hevc", isColorDepth10);
+ }
}
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
@@ -3940,6 +4190,11 @@ namespace MediaBrowser.Controller.MediaEncoding
if (videoStream != null)
{
+ if (videoStream.BitDepth.HasValue)
+ {
+ return videoStream.BitDepth.Value == 10;
+ }
+
if (!string.IsNullOrEmpty(videoStream.PixelFormat))
{
result = videoStream.PixelFormat.Contains("p10", StringComparison.OrdinalIgnoreCase);
@@ -3959,12 +4214,6 @@ namespace MediaBrowser.Controller.MediaEncoding
return true;
}
}
-
- result = (videoStream.BitDepth ?? 8) == 10;
- if (result)
- {
- return true;
- }
}
return result;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index fa9f40d60..b09b7dba6 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -422,7 +422,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
- return VideoStream?.Codec;
+ return VideoStream.Codec;
}
return OutputVideoCodec;
@@ -440,7 +440,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (EncodingHelper.IsCopyCodec(OutputAudioCodec))
{
- return AudioStream?.Codec;
+ return AudioStream.Codec;
}
return OutputAudioCodec;
@@ -568,7 +568,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- return forceDeinterlaceIfSourceIsInterlaced && isInputInterlaced;
+ return forceDeinterlaceIfSourceIsInterlaced;
}
public string[] GetRequestedProfiles(string codec)
diff --git a/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
new file mode 100644
index 000000000..7ce707b19
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
@@ -0,0 +1,23 @@
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ /// <summary>
+ /// Enum FilterOptionType.
+ /// </summary>
+ public enum FilterOptionType
+ {
+ /// <summary>
+ /// The scale_cuda_format.
+ /// </summary>
+ ScaleCudaFormat = 0,
+
+ /// <summary>
+ /// The tonemap_cuda_name.
+ /// </summary>
+ TonemapCudaName = 1,
+
+ /// <summary>
+ /// The tonemap_opencl_bt2390.
+ /// </summary>
+ TonemapOpenclBt2390 = 2
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index ff2456070..c5522bc3c 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -10,7 +10,6 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.System;
namespace MediaBrowser.Controller.MediaEncoding
{
@@ -20,11 +19,6 @@ namespace MediaBrowser.Controller.MediaEncoding
public interface IMediaEncoder : ITranscoderSupport
{
/// <summary>
- /// Gets location of the discovered FFmpeg tool.
- /// </summary>
- FFmpegLocation EncoderLocation { get; }
-
- /// <summary>
/// Gets the encoder path.
/// </summary>
/// <value>The encoder path.</value>
@@ -55,9 +49,21 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Whether given filter is supported.
/// </summary>
/// <param name="filter">The filter.</param>
+ /// <returns><c>true</c> if the filter is supported, <c>false</c> otherwise.</returns>
+ bool SupportsFilter(string filter);
+
+ /// <summary>
+ /// Whether filter is supported with the given option.
+ /// </summary>
/// <param name="option">The option.</param>
/// <returns><c>true</c> if the filter is supported, <c>false</c> otherwise.</returns>
- bool SupportsFilter(string filter, string option);
+ bool SupportsFilterWithOption(FilterOptionType option);
+
+ /// <summary>
+ /// Get the version of media encoder.
+ /// </summary>
+ /// <returns>The version of media encoder.</returns>
+ Version GetMediaEncoderVersion();
/// <summary>
/// Extracts the audio image.
diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
index b23c95112..c4ddc5618 100644
--- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
+++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs
@@ -6,7 +6,6 @@ using System;
using System.Globalization;
using System.IO;
using System.Text;
-using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@@ -121,7 +120,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var size = part.Split('=', 2)[^1];
int? scale = null;
- if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
+ if (size.Contains("kb", StringComparison.OrdinalIgnoreCase))
{
scale = 1024;
size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
@@ -140,7 +139,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var rate = part.Split('=', 2)[^1];
int? scale = null;
- if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
+ if (rate.Contains("kbits/s", StringComparison.OrdinalIgnoreCase))
{
scale = 1024;
rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs
index d15c6d318..a7da740e0 100644
--- a/MediaBrowser.Controller/Net/IAuthService.cs
+++ b/MediaBrowser.Controller/Net/IAuthService.cs
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
@@ -12,6 +13,6 @@ namespace MediaBrowser.Controller.Net
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Authorization information. Null if unauthenticated.</returns>
- AuthorizationInfo Authenticate(HttpRequest request);
+ Task<AuthorizationInfo> Authenticate(HttpRequest request);
}
}
diff --git a/MediaBrowser.Controller/Net/IAuthorizationContext.cs b/MediaBrowser.Controller/Net/IAuthorizationContext.cs
index 0d310548d..5c6ca43d1 100644
--- a/MediaBrowser.Controller/Net/IAuthorizationContext.cs
+++ b/MediaBrowser.Controller/Net/IAuthorizationContext.cs
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
@@ -11,14 +12,14 @@ namespace MediaBrowser.Controller.Net
/// Gets the authorization information.
/// </summary>
/// <param name="requestContext">The request context.</param>
- /// <returns>AuthorizationInfo.</returns>
- AuthorizationInfo GetAuthorizationInfo(HttpContext requestContext);
+ /// <returns>A task containing the authorization info.</returns>
+ Task<AuthorizationInfo> GetAuthorizationInfo(HttpContext requestContext);
/// <summary>
/// Gets the authorization information.
/// </summary>
/// <param name="requestContext">The request context.</param>
- /// <returns>AuthorizationInfo.</returns>
- AuthorizationInfo GetAuthorizationInfo(HttpRequest requestContext);
+ /// <returns>A <see cref="Task"/> containing the authorization info.</returns>
+ Task<AuthorizationInfo> GetAuthorizationInfo(HttpRequest requestContext);
}
}
diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs
index 6b896b41f..b48181b3f 100644
--- a/MediaBrowser.Controller/Net/ISessionContext.cs
+++ b/MediaBrowser.Controller/Net/ISessionContext.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
+using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Session;
using Microsoft.AspNetCore.Http;
@@ -8,12 +9,12 @@ namespace MediaBrowser.Controller.Net
{
public interface ISessionContext
{
- SessionInfo GetSession(object requestContext);
+ Task<SessionInfo> GetSession(object requestContext);
- User? GetUser(object requestContext);
+ Task<User?> GetUser(object requestContext);
- SessionInfo GetSession(HttpContext requestContext);
+ Task<SessionInfo> GetSession(HttpContext requestContext);
- User? GetUser(HttpContext requestContext);
+ Task<User?> GetUser(HttpContext requestContext);
}
}
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 0a9073e7f..a084f9196 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -49,17 +49,17 @@ namespace MediaBrowser.Controller.Persistence
/// <summary>
/// Gets chapters for an item.
/// </summary>
- /// <param name="id">The item.</param>
+ /// <param name="item">The item.</param>
/// <returns>The list of chapter info.</returns>
- List<ChapterInfo> GetChapters(BaseItem id);
+ List<ChapterInfo> GetChapters(BaseItem item);
/// <summary>
/// Gets a single chapter for an item.
/// </summary>
- /// <param name="id">The item.</param>
+ /// <param name="item">The item.</param>
/// <param name="index">The chapter index.</param>
/// <returns>The chapter info at the specified index.</returns>
- ChapterInfo GetChapter(BaseItem id, int index);
+ ChapterInfo GetChapter(BaseItem item, int index);
/// <summary>
/// Saves the chapters.
diff --git a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs
index ad34c8604..ec3706773 100644
--- a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs
+++ b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs
@@ -1,4 +1,7 @@
using System;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Model.QuickConnect;
namespace MediaBrowser.Controller.QuickConnect
@@ -16,8 +19,9 @@ namespace MediaBrowser.Controller.QuickConnect
/// <summary>
/// Initiates a new quick connect request.
/// </summary>
+ /// <param name="authorizationInfo">The initiator authorization info.</param>
/// <returns>A quick connect result with tokens to proceed or throws an exception if not active.</returns>
- QuickConnectResult TryConnect();
+ QuickConnectResult TryConnect(AuthorizationInfo authorizationInfo);
/// <summary>
/// Checks the status of an individual request.
@@ -32,6 +36,13 @@ namespace MediaBrowser.Controller.QuickConnect
/// <param name="userId">User id.</param>
/// <param name="code">Identifying code for the request.</param>
/// <returns>A boolean indicating if the authorization completed successfully.</returns>
- bool AuthorizeRequest(Guid userId, string code);
+ Task<bool> AuthorizeRequest(Guid userId, string code);
+
+ /// <summary>
+ /// Gets the authorized request for the secret.
+ /// </summary>
+ /// <param name="secret">The secret.</param>
+ /// <returns>The authentication result.</returns>
+ AuthenticationResult GetAuthorizedRequest(string secret);
}
}
diff --git a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs
deleted file mode 100644
index 3af6a525c..000000000
--- a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-
-namespace MediaBrowser.Controller.Security
-{
- public class AuthenticationInfoQuery
- {
- /// <summary>
- /// Gets or sets the device identifier.
- /// </summary>
- /// <value>The device identifier.</value>
- public string DeviceId { get; set; }
-
- /// <summary>
- /// Gets or sets the user identifier.
- /// </summary>
- /// <value>The user identifier.</value>
- public Guid UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the access token.
- /// </summary>
- /// <value>The access token.</value>
- public string AccessToken { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is active.
- /// </summary>
- /// <value><c>null</c> if [is active] contains no value, <c>true</c> if [is active]; otherwise, <c>false</c>.</value>
- public bool? IsActive { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance has user.
- /// </summary>
- /// <value><c>null</c> if [has user] contains no value, <c>true</c> if [has user]; otherwise, <c>false</c>.</value>
- public bool? HasUser { get; set; }
-
- /// <summary>
- /// Gets or sets the start index.
- /// </summary>
- /// <value>The start index.</value>
- public int? StartIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- /// <value>The limit.</value>
- public int? Limit { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Security/IAuthenticationManager.cs b/MediaBrowser.Controller/Security/IAuthenticationManager.cs
new file mode 100644
index 000000000..e3d18c8c0
--- /dev/null
+++ b/MediaBrowser.Controller/Security/IAuthenticationManager.cs
@@ -0,0 +1,33 @@
+#nullable enable
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Security
+{
+ /// <summary>
+ /// Handles the retrieval and storage of API keys.
+ /// </summary>
+ public interface IAuthenticationManager
+ {
+ /// <summary>
+ /// Creates an API key.
+ /// </summary>
+ /// <param name="name">The name of the key.</param>
+ /// <returns>A task representing the creation of the key.</returns>
+ Task CreateApiKey(string name);
+
+ /// <summary>
+ /// Gets the API keys.
+ /// </summary>
+ /// <returns>A task representing the retrieval of the API keys.</returns>
+ Task<IReadOnlyList<AuthenticationInfo>> GetApiKeys();
+
+ /// <summary>
+ /// Deletes an API key with the provided access token.
+ /// </summary>
+ /// <param name="accessToken">The access token.</param>
+ /// <returns>A task representing the deletion of the API key.</returns>
+ Task DeleteApiKey(string accessToken);
+ }
+}
diff --git a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs
deleted file mode 100644
index bd1289c1a..000000000
--- a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Controller.Security
-{
- public interface IAuthenticationRepository
- {
- /// <summary>
- /// Creates the specified information.
- /// </summary>
- /// <param name="info">The information.</param>
- void Create(AuthenticationInfo info);
-
- /// <summary>
- /// Updates the specified information.
- /// </summary>
- /// <param name="info">The information.</param>
- void Update(AuthenticationInfo info);
-
- /// <summary>
- /// Gets the specified query.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>QueryResult{AuthenticationInfo}.</returns>
- QueryResult<AuthenticationInfo> Get(AuthenticationInfoQuery query);
-
- void Delete(AuthenticationInfo info);
-
- DeviceOptions GetDeviceOptions(string deviceId);
-
- void UpdateDeviceOptions(string deviceId, DeviceOptions options);
- }
-}
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 0ff32fb53..c86556095 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -6,11 +6,10 @@ using System;
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.Controller.Security;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.SyncPlay;
@@ -83,8 +82,8 @@ namespace MediaBrowser.Controller.Session
/// <param name="deviceName">Name of the device.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
/// <param name="user">The user.</param>
- /// <returns>Session information.</returns>
- SessionInfo LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, Jellyfin.Data.Entities.User user);
+ /// <returns>A task containing the session information.</returns>
+ Task<SessionInfo> LogSessionActivity(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, Jellyfin.Data.Entities.User user);
/// <summary>
/// Used to report that a session controller has connected.
@@ -158,21 +157,21 @@ namespace MediaBrowser.Controller.Session
/// <summary>
/// Sends a SyncPlayCommand to a session.
/// </summary>
- /// <param name="session">The session.</param>
+ /// <param name="sessionId">The identifier of the session.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- Task SendSyncPlayCommand(SessionInfo session, SendCommand command, CancellationToken cancellationToken);
+ Task SendSyncPlayCommand(string sessionId, SendCommand command, CancellationToken cancellationToken);
/// <summary>
/// Sends a SyncPlayGroupUpdate to a session.
/// </summary>
- /// <param name="session">The session.</param>
+ /// <param name="sessionId">The identifier of the session.</param>
/// <param name="command">The group update.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <typeparam name="T">Type of group.</typeparam>
/// <returns>Task.</returns>
- Task SendSyncPlayGroupUpdate<T>(SessionInfo session, GroupUpdate<T> command, CancellationToken cancellationToken);
+ Task SendSyncPlayGroupUpdate<T>(string sessionId, GroupUpdate<T> command, CancellationToken cancellationToken);
/// <summary>
/// Sends the browse command.
@@ -280,33 +279,13 @@ namespace MediaBrowser.Controller.Session
void ReportNowViewingItem(string sessionId, string itemId);
/// <summary>
- /// Reports the now viewing item.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="item">The item.</param>
- void ReportNowViewingItem(string sessionId, BaseItemDto item);
-
- /// <summary>
/// Authenticates the new session.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task{SessionInfo}.</returns>
Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request);
- /// <summary>
- /// Authenticates a new session with quick connect.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="token">Quick connect access token.</param>
- /// <returns>Task{SessionInfo}.</returns>
- Task<AuthenticationResult> AuthenticateQuickConnect(AuthenticationRequest request, string token);
-
- /// <summary>
- /// Creates the new session.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task&lt;AuthenticationResult&gt;.</returns>
- Task<AuthenticationResult> CreateNewSession(AuthenticationRequest request);
+ Task<AuthenticationResult> AuthenticateDirect(AuthenticationRequest request);
/// <summary>
/// Reports the capabilities.
@@ -344,7 +323,7 @@ namespace MediaBrowser.Controller.Session
/// <param name="deviceId">The device identifier.</param>
/// <param name="remoteEndpoint">The remote endpoint.</param>
/// <returns>SessionInfo.</returns>
- SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint);
+ Task<SessionInfo> GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint);
/// <summary>
/// Gets the session by authentication token.
@@ -354,28 +333,24 @@ namespace MediaBrowser.Controller.Session
/// <param name="remoteEndpoint">The remote endpoint.</param>
/// <param name="appVersion">The application version.</param>
/// <returns>Task&lt;SessionInfo&gt;.</returns>
- SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion);
+ Task<SessionInfo> GetSessionByAuthenticationToken(Device info, string deviceId, string remoteEndpoint, string appVersion);
/// <summary>
/// Logouts the specified access token.
/// </summary>
/// <param name="accessToken">The access token.</param>
- void Logout(string accessToken);
+ /// <returns>A <see cref="Task"/> representing the log out process.</returns>
+ Task Logout(string accessToken);
- void Logout(AuthenticationInfo accessToken);
+ Task Logout(Device device);
/// <summary>
/// Revokes the user tokens.
/// </summary>
- /// <param name="userId">User ID.</param>
- /// <param name="currentAccessToken">Current access token.</param>
- void RevokeUserTokens(Guid userId, string currentAccessToken);
-
- /// <summary>
- /// Revokes the token.
- /// </summary>
- /// <param name="id">The identifier.</param>
- void RevokeToken(string id);
+ /// <param name="userId">The user's id.</param>
+ /// <param name="currentAccessToken">The current access token.</param>
+ /// <returns>Task.</returns>
+ Task RevokeUserTokens(Guid userId, string currentAccessToken);
void CloseIfNeeded(SessionInfo session);
}
diff --git a/MediaBrowser.Controller/SyncPlay/GroupMember.cs b/MediaBrowser.Controller/SyncPlay/GroupMember.cs
index 7e7e759a5..b973672c4 100644
--- a/MediaBrowser.Controller/SyncPlay/GroupMember.cs
+++ b/MediaBrowser.Controller/SyncPlay/GroupMember.cs
@@ -1,5 +1,6 @@
#nullable disable
+using System;
using MediaBrowser.Controller.Session;
namespace MediaBrowser.Controller.SyncPlay
@@ -15,14 +16,28 @@ namespace MediaBrowser.Controller.SyncPlay
/// <param name="session">The session.</param>
public GroupMember(SessionInfo session)
{
- Session = session;
+ SessionId = session.Id;
+ UserId = session.UserId;
+ UserName = session.UserName;
}
/// <summary>
- /// Gets the session.
+ /// Gets the identifier of the session.
/// </summary>
- /// <value>The session.</value>
- public SessionInfo Session { get; }
+ /// <value>The session identifier.</value>
+ public string SessionId { get; }
+
+ /// <summary>
+ /// Gets the identifier of the user.
+ /// </summary>
+ /// <value>The user identifier.</value>
+ public Guid UserId { get; }
+
+ /// <summary>
+ /// Gets the username.
+ /// </summary>
+ /// <value>The username.</value>
+ public string UserName { get; }
/// <summary>
/// Gets or sets the ping, in milliseconds.
diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs
index 91a13fb28..51c95a1bb 100644
--- a/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs
+++ b/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs
@@ -68,7 +68,16 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
/// <inheritdoc />
public virtual void HandleRequest(RemoveFromPlaylistGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
{
- var playingItemRemoved = context.RemoveFromPlayQueue(request.PlaylistItemIds);
+ bool playingItemRemoved;
+ if (request.ClearPlaylist)
+ {
+ context.ClearPlayQueue(request.ClearPlayingItem);
+ playingItemRemoved = request.ClearPlayingItem;
+ }
+ else
+ {
+ playingItemRemoved = context.RemoveFromPlayQueue(request.PlaylistItemIds);
+ }
var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.RemoveItems);
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
diff --git a/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs b/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs
index de26c7d9e..d2de22450 100644
--- a/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs
+++ b/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs
@@ -163,6 +163,12 @@ namespace MediaBrowser.Controller.SyncPlay
bool SetPlayingItem(Guid playlistItemId);
/// <summary>
+ /// Clears the play queue.
+ /// </summary>
+ /// <param name="clearPlayingItem">Whether to remove the playing item as well.</param>
+ void ClearPlayQueue(bool clearPlayingItem);
+
+ /// <summary>
/// Removes items from the play queue.
/// </summary>
/// <param name="playlistItemIds">The items to remove.</param>
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs
index 689145293..2f38d6adc 100644
--- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs
@@ -17,9 +17,13 @@ namespace MediaBrowser.Controller.SyncPlay.PlaybackRequests
/// Initializes a new instance of the <see cref="RemoveFromPlaylistGroupRequest"/> class.
/// </summary>
/// <param name="items">The playlist ids of the items to remove.</param>
- public RemoveFromPlaylistGroupRequest(IReadOnlyList<Guid> items)
+ /// <param name="clearPlaylist">Whether to clear the entire playlist. The items list will be ignored.</param>
+ /// <param name="clearPlayingItem">Whether to remove the playing item as well. Used only when clearing the playlist.</param>
+ public RemoveFromPlaylistGroupRequest(IReadOnlyList<Guid> items, bool clearPlaylist = false, bool clearPlayingItem = false)
{
PlaylistItemIds = items ?? Array.Empty<Guid>();
+ ClearPlaylist = clearPlaylist;
+ ClearPlayingItem = clearPlayingItem;
}
/// <summary>
@@ -28,6 +32,18 @@ namespace MediaBrowser.Controller.SyncPlay.PlaybackRequests
/// <value>The playlist identifiers ot the items.</value>
public IReadOnlyList<Guid> PlaylistItemIds { get; }
+ /// <summary>
+ /// Gets a value indicating whether the entire playlist should be cleared.
+ /// </summary>
+ /// <value>Whether the entire playlist should be cleared.</value>
+ public bool ClearPlaylist { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether the playing item should be removed as well.
+ /// </summary>
+ /// <value>Whether the playing item should be removed as well.</value>
+ public bool ClearPlayingItem { get; }
+
/// <inheritdoc />
public override PlaybackRequestType Action { get; } = PlaybackRequestType.RemoveFromPlaylist;