mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-02 22:08:27 +01:00
Merge remote-tracking branch 'upstream/master' into authenticationdb-efcore
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Jellyfin.Extensions;
|
||||
@@ -16,7 +15,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
||||
{
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
private int _metadataRefreshConcurrency = 0;
|
||||
private int _metadataRefreshConcurrency;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseItemManager"/> class.
|
||||
@@ -101,7 +100,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
||||
/// Called when the configuration is updated.
|
||||
/// It will refresh the metadata throttler if the relevant config changed.
|
||||
/// </summary>
|
||||
private void OnConfigurationUpdated(object sender, EventArgs e)
|
||||
private void OnConfigurationUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency();
|
||||
if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency)
|
||||
@@ -114,6 +113,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
||||
/// <summary>
|
||||
/// Creates the metadata refresh throttler.
|
||||
/// </summary>
|
||||
[MemberNotNull(nameof(MetadataRefreshThrottler))]
|
||||
private void SetupMetadataThrottler()
|
||||
{
|
||||
MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Threading;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
@@ -34,4 +32,4 @@ namespace MediaBrowser.Controller.BaseItemManager
|
||||
/// <returns><c>true</c> if image fetcher is enabled, else false.</returns>
|
||||
bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA1002, CA2227, CS1591
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Channels
|
||||
@@ -10,10 +9,10 @@ namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
public ChannelItemResult()
|
||||
{
|
||||
Items = new List<ChannelItemInfo>();
|
||||
Items = Array.Empty<ChannelItemInfo>();
|
||||
}
|
||||
|
||||
public List<ChannelItemInfo> Items { get; set; }
|
||||
public IReadOnlyList<ChannelItemInfo> Items { get; set; }
|
||||
|
||||
public int? TotalRecordCount { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA2227, CS1591
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -16,17 +14,17 @@ namespace MediaBrowser.Controller.Collections
|
||||
/// <summary>
|
||||
/// Occurs when [collection created].
|
||||
/// </summary>
|
||||
event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
|
||||
event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [items added to collection].
|
||||
/// </summary>
|
||||
event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
|
||||
event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [items removed from collection].
|
||||
/// </summary>
|
||||
event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
|
||||
event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the collection.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
@@ -22,7 +20,7 @@ namespace MediaBrowser.Controller.Dlna
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns>DeviceProfile.</returns>
|
||||
DeviceProfile GetProfile(IHeaderDictionary headers);
|
||||
DeviceProfile? GetProfile(IHeaderDictionary headers);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default profile.
|
||||
@@ -53,14 +51,14 @@ namespace MediaBrowser.Controller.Dlna
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>DeviceProfile.</returns>
|
||||
DeviceProfile GetProfile(string id);
|
||||
DeviceProfile? GetProfile(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile.
|
||||
/// </summary>
|
||||
/// <param name="deviceInfo">The device information.</param>
|
||||
/// <returns>DeviceProfile.</returns>
|
||||
DeviceProfile GetProfile(DeviceIdentification deviceInfo);
|
||||
DeviceProfile? GetProfile(DeviceIdentification deviceInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the server description XML.
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// </summary>
|
||||
/// <param name="source">The source object.</param>
|
||||
/// <param name="dest">The destination object.</param>
|
||||
/// <typeparam name="T">Source type.</typeparam>
|
||||
/// <typeparam name="TU">Destination type.</typeparam>
|
||||
public static void DeepCopy<T, TU>(this T source, TU dest)
|
||||
where T : BaseItem
|
||||
where TU : BaseItem
|
||||
@@ -109,6 +111,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// Copies all properties on newly created object. Skips properties that do not exist.
|
||||
/// </summary>
|
||||
/// <param name="source">The source object.</param>
|
||||
/// <typeparam name="T">Source type.</typeparam>
|
||||
/// <typeparam name="TU">Destination type.</typeparam>
|
||||
/// <returns>Destination object.</returns>
|
||||
public static TU DeepCopy<T, TU>(this T source)
|
||||
where T : BaseItem
|
||||
where TU : BaseItem, new()
|
||||
|
||||
@@ -15,6 +15,12 @@ namespace MediaBrowser.Controller.Entities
|
||||
[JsonIgnore]
|
||||
public virtual string CollectionType => null;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsInheritedParentImages => false;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsPeople => false;
|
||||
|
||||
public override bool CanDelete()
|
||||
{
|
||||
return false;
|
||||
@@ -24,11 +30,5 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsInheritedParentImages => false;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsPeople => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,23 @@ namespace MediaBrowser.Controller.Entities
|
||||
PhysicalFolderIds = Array.Empty<Guid>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display preferences id.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Allow different display preferences for each collection folder.
|
||||
/// </remarks>
|
||||
/// <value>The display prefs id.</value>
|
||||
[JsonIgnore]
|
||||
public override Guid DisplayPreferencesId => Id;
|
||||
|
||||
[JsonIgnore]
|
||||
public override string[] PhysicalLocations => PhysicalLocationsList;
|
||||
|
||||
public string[] PhysicalLocationsList { get; set; }
|
||||
|
||||
public Guid[] PhysicalFolderIds { get; set; }
|
||||
|
||||
public static IXmlSerializer XmlSerializer { get; set; }
|
||||
|
||||
public static IServerApplicationHost ApplicationHost { get; set; }
|
||||
@@ -63,6 +80,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
[JsonIgnore]
|
||||
public override IEnumerable<BaseItem> Children => GetActualChildren();
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsPeople => false;
|
||||
|
||||
public override bool CanDelete()
|
||||
{
|
||||
return false;
|
||||
@@ -160,23 +180,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display preferences id.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Allow different display preferences for each collection folder.
|
||||
/// </remarks>
|
||||
/// <value>The display prefs id.</value>
|
||||
[JsonIgnore]
|
||||
public override Guid DisplayPreferencesId => Id;
|
||||
|
||||
[JsonIgnore]
|
||||
public override string[] PhysicalLocations => PhysicalLocationsList;
|
||||
|
||||
public string[] PhysicalLocationsList { get; set; }
|
||||
|
||||
public Guid[] PhysicalFolderIds { get; set; }
|
||||
|
||||
public override bool IsSaveLocalMetadataEnabled()
|
||||
{
|
||||
return true;
|
||||
@@ -373,8 +376,5 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsPeople => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// Adds the trailer URL.
|
||||
/// </summary>
|
||||
/// <param name="item">Media item.</param>
|
||||
/// <param name="url">Trailer URL.</param>
|
||||
public static void AddTrailerUrl(this BaseItem item, string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
@@ -1669,7 +1669,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="datePlayed">The date played.</param>
|
||||
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
|
||||
/// <returns>Task.</returns>
|
||||
public override void MarkPlayed(
|
||||
User user,
|
||||
DateTime? datePlayed,
|
||||
@@ -1711,7 +1710,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// Marks the unplayed.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public override void MarkUnplayed(User user)
|
||||
{
|
||||
var itemsResult = GetItemList(new InternalItemsQuery
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// Gets the media sources.
|
||||
/// </summary>
|
||||
/// <param name="enablePathSubstitution"><c>true</c> to enable path substitution, <c>false</c> to not.</param>
|
||||
/// <returns>A list of media sources.</returns>
|
||||
List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
|
||||
|
||||
List<MediaStream> GetMediaStreams();
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// Gets the trailer count.
|
||||
/// </summary>
|
||||
/// <param name="item">Media item.</param>
|
||||
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
|
||||
public static int GetTrailerCount(this IHasTrailers item)
|
||||
=> item.LocalTrailerIds.Count + item.RemoteTrailerIds.Count;
|
||||
@@ -46,6 +47,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// Gets the trailer ids.
|
||||
/// </summary>
|
||||
/// <param name="item">Media item.</param>
|
||||
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
|
||||
public static IReadOnlyList<Guid> GetTrailerIds(this IHasTrailers item)
|
||||
{
|
||||
@@ -70,6 +72,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// Gets the trailers.
|
||||
/// </summary>
|
||||
/// <param name="item">Media item.</param>
|
||||
/// <returns><see cref="IReadOnlyList{BaseItem}" />.</returns>
|
||||
public static IReadOnlyList<BaseItem> GetTrailers(this IHasTrailers item)
|
||||
{
|
||||
|
||||
@@ -129,6 +129,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||
/// </summary>
|
||||
/// <param name="replaceAllMetadata"><c>true</c> to replace all metadata, <c>false</c> to not.</param>
|
||||
/// <returns><c>true</c> if changes were made, <c>false</c> if not.</returns>
|
||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||
{
|
||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||
|
||||
@@ -105,6 +105,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||
/// </summary>
|
||||
/// <param name="replaceAllMetadata"><c>true</c> to replace all metadata, <c>false</c> to not.</param>
|
||||
/// <returns><c>true</c> if changes were made, <c>false</c> if not.</returns>
|
||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||
{
|
||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||
|
||||
@@ -49,12 +49,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
/// <value>The index number.</value>
|
||||
public int? IndexNumberEnd { get; set; }
|
||||
|
||||
public string FindSeriesSortName()
|
||||
{
|
||||
var series = Series;
|
||||
return series == null ? SeriesName : series.SortName;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
protected override bool SupportsOwnedItems => IsStacked || MediaSourceCount > 1;
|
||||
|
||||
@@ -76,45 +70,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
[JsonIgnore]
|
||||
protected override bool EnableDefaultVideoUserDataKeys => false;
|
||||
|
||||
public override double GetDefaultPrimaryImageAspectRatio()
|
||||
{
|
||||
// hack for tv plugins
|
||||
if (SourceType == SourceType.Channel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 16.0 / 9;
|
||||
}
|
||||
|
||||
public override List<string> GetUserDataKeys()
|
||||
{
|
||||
var list = base.GetUserDataKeys();
|
||||
|
||||
var series = Series;
|
||||
if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
|
||||
{
|
||||
var seriesUserDataKeys = series.GetUserDataKeys();
|
||||
var take = seriesUserDataKeys.Count;
|
||||
if (seriesUserDataKeys.Count > 1)
|
||||
{
|
||||
take--;
|
||||
}
|
||||
|
||||
var newList = seriesUserDataKeys.GetRange(0, take);
|
||||
var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture);
|
||||
for (int i = 0; i < take; i++)
|
||||
{
|
||||
newList[i] = newList[i] + suffix;
|
||||
}
|
||||
|
||||
newList.AddRange(list);
|
||||
list = newList;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Episode's Series Instance.
|
||||
/// </summary>
|
||||
@@ -161,6 +116,74 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
[JsonIgnore]
|
||||
public string SeasonName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsRemoteImageDownloading
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsMissingEpisode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsMissingEpisode => LocationType == LocationType.Virtual;
|
||||
|
||||
[JsonIgnore]
|
||||
public Guid SeasonId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Guid SeriesId { get; set; }
|
||||
|
||||
public string FindSeriesSortName()
|
||||
{
|
||||
var series = Series;
|
||||
return series == null ? SeriesName : series.SortName;
|
||||
}
|
||||
|
||||
public override double GetDefaultPrimaryImageAspectRatio()
|
||||
{
|
||||
// hack for tv plugins
|
||||
if (SourceType == SourceType.Channel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 16.0 / 9;
|
||||
}
|
||||
|
||||
public override List<string> GetUserDataKeys()
|
||||
{
|
||||
var list = base.GetUserDataKeys();
|
||||
|
||||
var series = Series;
|
||||
if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
|
||||
{
|
||||
var seriesUserDataKeys = series.GetUserDataKeys();
|
||||
var take = seriesUserDataKeys.Count;
|
||||
if (seriesUserDataKeys.Count > 1)
|
||||
{
|
||||
take--;
|
||||
}
|
||||
|
||||
var newList = seriesUserDataKeys.GetRange(0, take);
|
||||
var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture);
|
||||
for (int i = 0; i < take; i++)
|
||||
{
|
||||
newList[i] = newList[i] + suffix;
|
||||
}
|
||||
|
||||
newList.AddRange(list);
|
||||
list = newList;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public string FindSeriesPresentationUniqueKey()
|
||||
{
|
||||
var series = Series;
|
||||
@@ -242,29 +265,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
return false;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsRemoteImageDownloading
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsMissingEpisode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsMissingEpisode => LocationType == LocationType.Virtual;
|
||||
|
||||
[JsonIgnore]
|
||||
public Guid SeasonId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Guid SeriesId { get; set; }
|
||||
|
||||
public Guid FindSeriesId()
|
||||
{
|
||||
var series = FindParent<Series>();
|
||||
|
||||
@@ -38,6 +38,50 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
[JsonIgnore]
|
||||
public override Guid DisplayParentId => SeriesId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets this Episode's Series Instance.
|
||||
/// </summary>
|
||||
/// <value>The series.</value>
|
||||
[JsonIgnore]
|
||||
public Series Series
|
||||
{
|
||||
get
|
||||
{
|
||||
var seriesId = SeriesId;
|
||||
if (seriesId == Guid.Empty)
|
||||
{
|
||||
seriesId = FindSeriesId();
|
||||
}
|
||||
|
||||
return seriesId == Guid.Empty ? null : (LibraryManager.GetItemById(seriesId) as Series);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string SeriesPath
|
||||
{
|
||||
get
|
||||
{
|
||||
var series = Series;
|
||||
|
||||
if (series != null)
|
||||
{
|
||||
return series.Path;
|
||||
}
|
||||
|
||||
return System.IO.Path.GetDirectoryName(Path);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string SeriesPresentationUniqueKey { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string SeriesName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Guid SeriesId { get; set; }
|
||||
|
||||
public override double GetDefaultPrimaryImageAspectRatio()
|
||||
{
|
||||
double value = 2;
|
||||
@@ -80,41 +124,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets this Episode's Series Instance.
|
||||
/// </summary>
|
||||
/// <value>The series.</value>
|
||||
[JsonIgnore]
|
||||
public Series Series
|
||||
{
|
||||
get
|
||||
{
|
||||
var seriesId = SeriesId;
|
||||
if (seriesId == Guid.Empty)
|
||||
{
|
||||
seriesId = FindSeriesId();
|
||||
}
|
||||
|
||||
return seriesId == Guid.Empty ? null : (LibraryManager.GetItemById(seriesId) as Series);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string SeriesPath
|
||||
{
|
||||
get
|
||||
{
|
||||
var series = Series;
|
||||
|
||||
if (series != null)
|
||||
{
|
||||
return series.Path;
|
||||
}
|
||||
|
||||
return System.IO.Path.GetDirectoryName(Path);
|
||||
}
|
||||
}
|
||||
|
||||
public override string CreatePresentationUniqueKey()
|
||||
{
|
||||
if (IndexNumber.HasValue)
|
||||
@@ -157,6 +166,9 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
/// <summary>
|
||||
/// Gets the episodes.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="options">The options to use.</param>
|
||||
/// <returns>Set of episodes.</returns>
|
||||
public List<BaseItem> GetEpisodes(User user, DtoOptions options)
|
||||
{
|
||||
return GetEpisodes(Series, user, options);
|
||||
@@ -193,15 +205,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
return UnratedItem.Series;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string SeriesPresentationUniqueKey { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string SeriesName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Guid SeriesId { get; set; }
|
||||
|
||||
public string FindSeriesPresentationUniqueKey()
|
||||
{
|
||||
var series = Series;
|
||||
@@ -241,6 +244,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
/// <summary>
|
||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||
/// </summary>
|
||||
/// <param name="replaceAllMetadata"><c>true</c> to replace metdata, <c>false</c> to not.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||
{
|
||||
|
||||
@@ -72,6 +72,9 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
/// <value>The status.</value>
|
||||
public SeriesStatus? Status { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool StopRefreshIfLocalMetadataFound => false;
|
||||
|
||||
public override double GetDefaultPrimaryImageAspectRatio()
|
||||
{
|
||||
double value = 2;
|
||||
@@ -293,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;
|
||||
}
|
||||
@@ -394,6 +397,10 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
/// <summary>
|
||||
/// Filters the episodes by season.
|
||||
/// </summary>
|
||||
/// <param name="episodes">The episodes.</param>
|
||||
/// <param name="parentSeason">The season.</param>
|
||||
/// <param name="includeSpecials"><c>true</c> to include special, <c>false</c> to not.</param>
|
||||
/// <returns>The set of episodes.</returns>
|
||||
public static IEnumerable<BaseItem> FilterEpisodesBySeason(IEnumerable<BaseItem> episodes, Season parentSeason, bool includeSpecials)
|
||||
{
|
||||
var seasonNumber = parentSeason.IndexNumber;
|
||||
@@ -424,6 +431,10 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
/// <summary>
|
||||
/// Filters the episodes by season.
|
||||
/// </summary>
|
||||
/// <param name="episodes">The episodes.</param>
|
||||
/// <param name="seasonNumber">The season.</param>
|
||||
/// <param name="includeSpecials"><c>true</c> to include special, <c>false</c> to not.</param>
|
||||
/// <returns>The set of episodes.</returns>
|
||||
public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
|
||||
{
|
||||
if (!includeSpecials || seasonNumber < 1)
|
||||
@@ -499,8 +510,5 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool StopRefreshIfLocalMetadataFound => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,13 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// </summary>
|
||||
public class UserItemData
|
||||
{
|
||||
public const double MinLikeValue = 6.5;
|
||||
|
||||
/// <summary>
|
||||
/// The _rating.
|
||||
/// </summary>
|
||||
private double? _rating;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
@@ -24,11 +31,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <value>The key.</value>
|
||||
public string Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _rating.
|
||||
/// </summary>
|
||||
private double? _rating;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the users 0-10 rating.
|
||||
/// </summary>
|
||||
@@ -93,8 +95,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <value>The index of the subtitle stream.</value>
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
public const double MinLikeValue = 6.5;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the item is liked or not.
|
||||
/// This should never be serialized.
|
||||
|
||||
@@ -21,8 +21,28 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// </summary>
|
||||
public class UserRootFolder : Folder
|
||||
{
|
||||
private List<Guid> _childrenIds = null;
|
||||
private readonly object _childIdsLock = new object();
|
||||
private List<Guid> _childrenIds = null;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsInheritedParentImages => false;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsPlayedStatus => false;
|
||||
|
||||
[JsonIgnore]
|
||||
protected override bool SupportsShortcutChildren => true;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool IsPreSorted => true;
|
||||
|
||||
private void ClearCache()
|
||||
{
|
||||
lock (_childIdsLock)
|
||||
{
|
||||
_childrenIds = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override List<BaseItem> LoadChildren()
|
||||
{
|
||||
@@ -39,20 +59,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsInheritedParentImages => false;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsPlayedStatus => false;
|
||||
|
||||
private void ClearCache()
|
||||
{
|
||||
lock (_childIdsLock)
|
||||
{
|
||||
_childrenIds = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
||||
{
|
||||
if (query.Recursive)
|
||||
@@ -74,12 +80,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
return GetChildren(user, true).Count;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
protected override bool SupportsShortcutChildren => true;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool IsPreSorted => true;
|
||||
|
||||
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
|
||||
{
|
||||
var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
|
||||
|
||||
@@ -28,6 +28,14 @@ namespace MediaBrowser.Controller.Entities
|
||||
ISupportsPlaceHolders,
|
||||
IHasMediaSources
|
||||
{
|
||||
public Video()
|
||||
{
|
||||
AdditionalParts = Array.Empty<string>();
|
||||
LocalAlternateVersions = Array.Empty<string>();
|
||||
SubtitleFiles = Array.Empty<string>();
|
||||
LinkedAlternateVersions = Array.Empty<LinkedChild>();
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string PrimaryVersionId { get; set; }
|
||||
|
||||
@@ -74,30 +82,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPrimaryVersionId(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
PrimaryVersionId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrimaryVersionId = id;
|
||||
}
|
||||
|
||||
PresentationUniqueKey = CreatePresentationUniqueKey();
|
||||
}
|
||||
|
||||
public override string CreatePresentationUniqueKey()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PrimaryVersionId))
|
||||
{
|
||||
return PrimaryVersionId;
|
||||
}
|
||||
|
||||
return base.CreatePresentationUniqueKey();
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsThemeMedia => true;
|
||||
|
||||
@@ -151,24 +135,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <value>The aspect ratio.</value>
|
||||
public string AspectRatio { get; set; }
|
||||
|
||||
public Video()
|
||||
{
|
||||
AdditionalParts = Array.Empty<string>();
|
||||
LocalAlternateVersions = Array.Empty<string>();
|
||||
SubtitleFiles = Array.Empty<string>();
|
||||
LinkedAlternateVersions = Array.Empty<LinkedChild>();
|
||||
}
|
||||
|
||||
public override bool CanDownload()
|
||||
{
|
||||
if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsFileProtocol;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsAddingToPlaylist => true;
|
||||
|
||||
@@ -196,16 +162,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
[JsonIgnore]
|
||||
public override bool HasLocalAlternateVersions => LocalAlternateVersions.Length > 0;
|
||||
|
||||
public IEnumerable<Guid> GetAdditionalPartIds()
|
||||
{
|
||||
return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetLocalAlternateVersionIds()
|
||||
{
|
||||
return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
||||
}
|
||||
|
||||
public static ILiveTvManager LiveTvManager { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
@@ -222,21 +178,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsActiveRecording()
|
||||
{
|
||||
return LiveTvManager.GetActiveRecordingInfo(Path) != null;
|
||||
}
|
||||
|
||||
public override bool CanDelete()
|
||||
{
|
||||
if (IsActiveRecording())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CanDelete();
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsCompleteMedia
|
||||
{
|
||||
@@ -254,80 +195,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
[JsonIgnore]
|
||||
protected virtual bool EnableDefaultVideoUserDataKeys => true;
|
||||
|
||||
public override List<string> GetUserDataKeys()
|
||||
{
|
||||
var list = base.GetUserDataKeys();
|
||||
|
||||
if (EnableDefaultVideoUserDataKeys)
|
||||
{
|
||||
if (ExtraType.HasValue)
|
||||
{
|
||||
var key = this.GetProviderId(MetadataProvider.Tmdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, GetUserDataKey(key));
|
||||
}
|
||||
|
||||
key = this.GetProviderId(MetadataProvider.Imdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, GetUserDataKey(key));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = this.GetProviderId(MetadataProvider.Imdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, key);
|
||||
}
|
||||
|
||||
key = this.GetProviderId(MetadataProvider.Tmdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private string GetUserDataKey(string providerId)
|
||||
{
|
||||
var key = providerId + "-" + ExtraType.ToString().ToLowerInvariant();
|
||||
|
||||
// Make sure different trailers have their own data.
|
||||
if (RunTimeTicks.HasValue)
|
||||
{
|
||||
key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public IEnumerable<Video> GetLinkedAlternateVersions()
|
||||
{
|
||||
return LinkedAlternateVersions
|
||||
.Select(GetLinkedChild)
|
||||
.Where(i => i != null)
|
||||
.OfType<Video>()
|
||||
.OrderBy(i => i.SortName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional parts.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{Video}.</returns>
|
||||
public IOrderedEnumerable<Video> GetAdditionalParts()
|
||||
{
|
||||
return GetAdditionalPartIds()
|
||||
.Select(i => LibraryManager.GetItemById(i))
|
||||
.Where(i => i != null)
|
||||
.OfType<Video>()
|
||||
.OrderBy(i => i.SortName);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override string ContainingFolderPath
|
||||
{
|
||||
@@ -369,6 +236,153 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [is3 D].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [is3 D]; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool Is3D => Video3DFormat.HasValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the media.
|
||||
/// </summary>
|
||||
/// <value>The type of the media.</value>
|
||||
[JsonIgnore]
|
||||
public override string MediaType => Model.Entities.MediaType.Video;
|
||||
|
||||
public override List<string> GetUserDataKeys()
|
||||
{
|
||||
var list = base.GetUserDataKeys();
|
||||
|
||||
if (EnableDefaultVideoUserDataKeys)
|
||||
{
|
||||
if (ExtraType.HasValue)
|
||||
{
|
||||
var key = this.GetProviderId(MetadataProvider.Tmdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, GetUserDataKey(key));
|
||||
}
|
||||
|
||||
key = this.GetProviderId(MetadataProvider.Imdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, GetUserDataKey(key));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = this.GetProviderId(MetadataProvider.Imdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, key);
|
||||
}
|
||||
|
||||
key = this.GetProviderId(MetadataProvider.Tmdb);
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
list.Insert(0, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public void SetPrimaryVersionId(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
PrimaryVersionId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrimaryVersionId = id;
|
||||
}
|
||||
|
||||
PresentationUniqueKey = CreatePresentationUniqueKey();
|
||||
}
|
||||
|
||||
public override string CreatePresentationUniqueKey()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PrimaryVersionId))
|
||||
{
|
||||
return PrimaryVersionId;
|
||||
}
|
||||
|
||||
return base.CreatePresentationUniqueKey();
|
||||
}
|
||||
|
||||
public override bool CanDownload()
|
||||
{
|
||||
if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsFileProtocol;
|
||||
}
|
||||
|
||||
protected override bool IsActiveRecording()
|
||||
{
|
||||
return LiveTvManager.GetActiveRecordingInfo(Path) != null;
|
||||
}
|
||||
|
||||
public override bool CanDelete()
|
||||
{
|
||||
if (IsActiveRecording())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CanDelete();
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetAdditionalPartIds()
|
||||
{
|
||||
return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetLocalAlternateVersionIds()
|
||||
{
|
||||
return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
||||
}
|
||||
|
||||
private string GetUserDataKey(string providerId)
|
||||
{
|
||||
var key = providerId + "-" + ExtraType.ToString().ToLowerInvariant();
|
||||
|
||||
// Make sure different trailers have their own data.
|
||||
if (RunTimeTicks.HasValue)
|
||||
{
|
||||
key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public IEnumerable<Video> GetLinkedAlternateVersions()
|
||||
{
|
||||
return LinkedAlternateVersions
|
||||
.Select(GetLinkedChild)
|
||||
.Where(i => i != null)
|
||||
.OfType<Video>()
|
||||
.OrderBy(i => i.SortName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional parts.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{Video}.</returns>
|
||||
public IOrderedEnumerable<Video> GetAdditionalParts()
|
||||
{
|
||||
return GetAdditionalPartIds()
|
||||
.Select(i => LibraryManager.GetItemById(i))
|
||||
.Where(i => i != null)
|
||||
.OfType<Video>()
|
||||
.OrderBy(i => i.SortName);
|
||||
}
|
||||
|
||||
internal override ItemUpdateType UpdateFromResolvedItem(BaseItem newItem)
|
||||
{
|
||||
var updateType = base.UpdateFromResolvedItem(newItem);
|
||||
@@ -397,20 +411,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
return updateType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [is3 D].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [is3 D]; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool Is3D => Video3DFormat.HasValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the media.
|
||||
/// </summary>
|
||||
/// <value>The type of the media.</value>
|
||||
[JsonIgnore]
|
||||
public override string MediaType => Model.Entities.MediaType.Video;
|
||||
|
||||
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||
{
|
||||
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
@@ -16,9 +15,7 @@ using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
@@ -40,6 +37,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
"ConstrainedHigh"
|
||||
};
|
||||
|
||||
private static readonly Version minVersionForCudaOverlay = new Version(4, 4);
|
||||
|
||||
public EncodingHelper(
|
||||
IMediaEncoder mediaEncoder,
|
||||
ISubtitleEncoder subtitleEncoder)
|
||||
@@ -109,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)
|
||||
@@ -135,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.
|
||||
@@ -500,14 +525,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;
|
||||
@@ -515,26 +545,24 @@ 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(options.VaapiDevice)
|
||||
.Append(" -init_hw_device opencl=ocl@va ")
|
||||
.Append("-hwaccel_device va ")
|
||||
.Append("-hwaccel_output_format vaapi ")
|
||||
.Append("-filter_hw_device ocl ");
|
||||
@@ -543,14 +571,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
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(' ');
|
||||
}
|
||||
|
||||
@@ -558,7 +586,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;
|
||||
|
||||
@@ -594,9 +622,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 ");
|
||||
}
|
||||
|
||||
@@ -605,7 +632,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
|
||||
@@ -613,22 +640,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 (isTonemappingSupported)
|
||||
if (!isCudaTonemappingSupported && 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, "amf", StringComparison.OrdinalIgnoreCase)
|
||||
&& (isD3d11vaDecoder || isSwDecoder))
|
||||
{
|
||||
if (isOpenclTonemappingSupported)
|
||||
{
|
||||
arg.Append("-init_hw_device opencl=ocl:")
|
||||
.Append(options.OpenclDevice)
|
||||
.Append(" -filter_hw_device ocl ");
|
||||
}
|
||||
}
|
||||
|
||||
if (state.IsVideoRequest
|
||||
&& string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
arg.Append("-hwaccel videotoolbox ");
|
||||
}
|
||||
@@ -1762,7 +1798,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
var request = state.BaseRequest;
|
||||
|
||||
var inputChannels = audioStream?.Channels;
|
||||
var inputChannels = audioStream.Channels;
|
||||
|
||||
if (inputChannels <= 0)
|
||||
{
|
||||
@@ -2013,14 +2049,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);
|
||||
}
|
||||
@@ -2030,8 +2070,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)
|
||||
@@ -2046,13 +2086,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 ?
|
||||
@@ -2065,9 +2114,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)
|
||||
@@ -2078,9 +2127,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
|
||||
@@ -2093,9 +2142,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))
|
||||
@@ -2112,16 +2161,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(
|
||||
@@ -2218,11 +2276,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";
|
||||
@@ -2273,15 +2331,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";
|
||||
}
|
||||
@@ -2559,16 +2625,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;
|
||||
@@ -2580,19 +2651,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)
|
||||
@@ -2664,7 +2741,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,
|
||||
@@ -2672,6 +2753,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,
|
||||
@@ -2683,7 +2772,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");
|
||||
@@ -2699,12 +2792,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");
|
||||
@@ -2812,6 +2911,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)
|
||||
@@ -2821,10 +2975,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)
|
||||
@@ -2833,7 +2987,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
if (isCudaFormatConversionSupported)
|
||||
{
|
||||
if (isLibX264Encoder || isLibX265Encoder || hasSubs)
|
||||
if (isLibX264Encoder
|
||||
|| isLibX265Encoder
|
||||
|| hasTextSubs
|
||||
|| (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder))
|
||||
{
|
||||
if (isNvencEncoder)
|
||||
{
|
||||
@@ -2860,7 +3017,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)
|
||||
{
|
||||
@@ -2881,7 +3042,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");
|
||||
}
|
||||
@@ -2927,6 +3088,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)
|
||||
{
|
||||
@@ -2939,6 +3111,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>
|
||||
@@ -3104,7 +3306,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;
|
||||
@@ -3113,7 +3315,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)
|
||||
{
|
||||
@@ -3409,8 +3611,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))
|
||||
@@ -3943,6 +4150,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);
|
||||
@@ -3962,12 +4174,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result = (videoStream.BitDepth ?? 8) == 10;
|
||||
if (result)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -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)
|
||||
|
||||
23
MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
Normal file
23
MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -19,11 +18,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
/// </summary>
|
||||
public interface IMediaEncoder : ITranscoderSupport
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets location of the discovered FFmpeg tool.
|
||||
/// </summary>
|
||||
FFmpegLocation EncoderLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the encoder path.
|
||||
/// </summary>
|
||||
@@ -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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable CS1591, SA1306, SA1401
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -30,6 +30,21 @@ namespace MediaBrowser.Controller.Net
|
||||
private readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>> _activeConnections =
|
||||
new List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>>();
|
||||
|
||||
/// <summary>
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
protected ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> Logger;
|
||||
|
||||
protected BasePeriodicWebSocketListener(ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> logger)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type used for the messages sent to the client.
|
||||
/// </summary>
|
||||
@@ -54,21 +69,6 @@ namespace MediaBrowser.Controller.Net
|
||||
/// <returns>Task{`1}.</returns>
|
||||
protected abstract Task<TReturnDataType> GetDataToSend();
|
||||
|
||||
/// <summary>
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
protected ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> Logger;
|
||||
|
||||
protected BasePeriodicWebSocketListener(ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> logger)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the message.
|
||||
/// </summary>
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace MediaBrowser.Controller.Persistence
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="userData">The user data.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,24 +31,18 @@ namespace MediaBrowser.Controller.Playlists
|
||||
".zpl"
|
||||
};
|
||||
|
||||
public Guid OwnerUserId { get; set; }
|
||||
|
||||
public Share[] Shares { get; set; }
|
||||
|
||||
public Playlist()
|
||||
{
|
||||
Shares = Array.Empty<Share>();
|
||||
}
|
||||
|
||||
public Guid OwnerUserId { get; set; }
|
||||
|
||||
public Share[] Shares { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsFile => IsPlaylistFile(Path);
|
||||
|
||||
public static bool IsPlaylistFile(string path)
|
||||
{
|
||||
// The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot).
|
||||
return System.IO.Path.HasExtension(path) && !Directory.Exists(path);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override string ContainingFolderPath
|
||||
{
|
||||
@@ -80,6 +74,41 @@ namespace MediaBrowser.Controller.Playlists
|
||||
[JsonIgnore]
|
||||
public override bool SupportsCumulativeRunTimeTicks => true;
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool IsPreSorted => true;
|
||||
|
||||
public string PlaylistMediaType { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public override string MediaType => PlaylistMediaType;
|
||||
|
||||
[JsonIgnore]
|
||||
private bool IsSharedItem
|
||||
{
|
||||
get
|
||||
{
|
||||
var path = Path;
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, path);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPlaylistFile(string path)
|
||||
{
|
||||
// The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot).
|
||||
return System.IO.Path.HasExtension(path) && !Directory.Exists(path);
|
||||
}
|
||||
|
||||
public void SetMediaType(string value)
|
||||
{
|
||||
PlaylistMediaType = value;
|
||||
}
|
||||
|
||||
public override double GetDefaultPrimaryImageAspectRatio()
|
||||
{
|
||||
return 1;
|
||||
@@ -197,35 +226,6 @@ namespace MediaBrowser.Controller.Playlists
|
||||
return new[] { item };
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool IsPreSorted => true;
|
||||
|
||||
public string PlaylistMediaType { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public override string MediaType => PlaylistMediaType;
|
||||
|
||||
public void SetMediaType(string value)
|
||||
{
|
||||
PlaylistMediaType = value;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private bool IsSharedItem
|
||||
{
|
||||
get
|
||||
{
|
||||
var path = Path;
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, path);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsVisible(User user)
|
||||
{
|
||||
if (!IsSharedItem)
|
||||
|
||||
@@ -13,18 +13,18 @@ namespace MediaBrowser.Controller.Resolvers
|
||||
/// </summary>
|
||||
public interface IItemResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
ResolverPriority Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the path.
|
||||
/// </summary>
|
||||
/// <param name="args">The args.</param>
|
||||
/// <returns>BaseItem.</returns>
|
||||
BaseItem ResolvePath(ItemResolveArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
ResolverPriority Priority { get; }
|
||||
}
|
||||
|
||||
public interface IMultiItemResolver
|
||||
@@ -38,14 +38,14 @@ namespace MediaBrowser.Controller.Resolvers
|
||||
|
||||
public class MultiItemResolverResult
|
||||
{
|
||||
public List<BaseItem> Items { get; set; }
|
||||
|
||||
public List<FileSystemMetadata> ExtraFiles { get; set; }
|
||||
|
||||
public MultiItemResolverResult()
|
||||
{
|
||||
Items = new List<BaseItem>();
|
||||
ExtraFiles = new List<FileSystemMetadata>();
|
||||
}
|
||||
|
||||
public List<BaseItem> Items { get; set; }
|
||||
|
||||
public List<FileSystemMetadata> ExtraFiles { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
/// <summary>
|
||||
/// Searches the subtitles.
|
||||
/// </summary>
|
||||
/// <param name="video">The video.</param>
|
||||
/// <param name="language">Subtitle language.</param>
|
||||
/// <param name="isPerfectMatch">Require perfect match.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for the operation.</param>
|
||||
/// <returns>Subtitles, wrapped in task.</returns>
|
||||
Task<RemoteSubtitleInfo[]> SearchSubtitles(
|
||||
Video video,
|
||||
string language,
|
||||
@@ -47,11 +52,20 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
/// <summary>
|
||||
/// Downloads the subtitles.
|
||||
/// </summary>
|
||||
/// <param name="video">The video.</param>
|
||||
/// <param name="subtitleId">Subtitle ID.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for the operation.</param>
|
||||
/// <returns>A task.</returns>
|
||||
Task DownloadSubtitles(Video video, string subtitleId, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the subtitles.
|
||||
/// </summary>
|
||||
/// <param name="video">The video.</param>
|
||||
/// <param name="libraryOptions">Library options to use.</param>
|
||||
/// <param name="subtitleId">Subtitle ID.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for the operation.</param>
|
||||
/// <returns>A task.</returns>
|
||||
Task DownloadSubtitles(Video video, LibraryOptions libraryOptions, string subtitleId, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
@@ -73,11 +87,16 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
/// <summary>
|
||||
/// Deletes the subtitles.
|
||||
/// </summary>
|
||||
/// <param name="item">Media item.</param>
|
||||
/// <param name="index">Subtitle index.</param>
|
||||
/// <returns>A task.</returns>
|
||||
Task DeleteSubtitles(BaseItem item, int index);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the providers.
|
||||
/// </summary>
|
||||
/// <param name="item">The media item.</param>
|
||||
/// <returns>Subtitles providers.</returns>
|
||||
SubtitleProviderInfo[] GetSupportedProviders(BaseItem item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,15 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
{
|
||||
public class SubtitleSearchRequest : IHasProviderIds
|
||||
{
|
||||
public SubtitleSearchRequest()
|
||||
{
|
||||
SearchAllProviders = true;
|
||||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
DisabledSubtitleFetchers = Array.Empty<string>();
|
||||
SubtitleFetcherOrder = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
public string TwoLetterISOLanguageName { get; set; }
|
||||
@@ -42,14 +51,5 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
public string[] DisabledSubtitleFetchers { get; set; }
|
||||
|
||||
public string[] SubtitleFetcherOrder { get; set; }
|
||||
|
||||
public SubtitleSearchRequest()
|
||||
{
|
||||
SearchAllProviders = true;
|
||||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
DisabledSubtitleFetchers = Array.Empty<string>();
|
||||
SubtitleFetcherOrder = Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user