aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
authorClaus Vium <cvium@users.noreply.github.com>2020-12-04 13:50:44 +0100
committerGitHub <noreply@github.com>2020-12-04 13:50:44 +0100
commitdca3f62ff85ba62af95831848718e9764d163306 (patch)
tree5be2338b7ec18013fb18feb3325d3228b3e5da59 /MediaBrowser.Controller
parenteed1a40b1935e00e04f6b27c7d307bf110b83b31 (diff)
parent9afd19b06e025992ee5159a08d7af8bd736a828e (diff)
Merge branch 'master' into PlugsVersionNumberFix
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs51
-rw-r--r--MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs9
-rw-r--r--MediaBrowser.Controller/Channels/IChannelManager.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs1
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs226
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs6
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs45
-rw-r--r--MediaBrowser.Controller/IResourceFileManager.cs9
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs44
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs4
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs224
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs5
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs15
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs33
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs11
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs5
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs8
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs5
21 files changed, 392 insertions, 318 deletions
diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
index 67aa7f338..085f769d0 100644
--- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
@@ -1,5 +1,7 @@
-using System;
+using System;
using System.Linq;
+using System.Threading;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -12,6 +14,8 @@ namespace MediaBrowser.Controller.BaseItemManager
{
private readonly IServerConfigurationManager _serverConfigurationManager;
+ private int _metadataRefreshConcurrency = 0;
+
/// <summary>
/// Initializes a new instance of the <see cref="BaseItemManager"/> class.
/// </summary>
@@ -19,9 +23,17 @@ namespace MediaBrowser.Controller.BaseItemManager
public BaseItemManager(IServerConfigurationManager serverConfigurationManager)
{
_serverConfigurationManager = serverConfigurationManager;
+
+ _metadataRefreshConcurrency = GetMetadataRefreshConcurrency();
+ SetupMetadataThrottler();
+
+ _serverConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
}
/// <inheritdoc />
+ public SemaphoreSlim MetadataRefreshThrottler { get; private set; }
+
+ /// <inheritdoc />
public bool IsMetadataFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name)
{
if (baseItem is Channel)
@@ -82,5 +94,42 @@ namespace MediaBrowser.Controller.BaseItemManager
return itemConfig == null || !itemConfig.DisabledImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase);
}
+
+ /// <summary>
+ /// 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)
+ {
+ int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency();
+ if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency)
+ {
+ _metadataRefreshConcurrency = newMetadataRefreshConcurrency;
+ SetupMetadataThrottler();
+ }
+ }
+
+ /// <summary>
+ /// Creates the metadata refresh throttler.
+ /// </summary>
+ private void SetupMetadataThrottler()
+ {
+ MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency);
+ }
+
+ /// <summary>
+ /// Returns the metadata refresh concurrency.
+ /// </summary>
+ private int GetMetadataRefreshConcurrency()
+ {
+ var concurrency = _serverConfigurationManager.Configuration.LibraryMetadataRefreshConcurrency;
+
+ if (concurrency <= 0)
+ {
+ concurrency = Environment.ProcessorCount;
+ }
+
+ return concurrency;
+ }
}
}
diff --git a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
index ee4d3dcdc..e1f5d05a6 100644
--- a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Entities;
+using System;
+using System.Threading;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Controller.BaseItemManager
@@ -9,6 +11,11 @@ namespace MediaBrowser.Controller.BaseItemManager
public interface IBaseItemManager
{
/// <summary>
+ /// Gets the semaphore used to limit the amount of concurrent metadata refreshes.
+ /// </summary>
+ SemaphoreSlim MetadataRefreshThrottler { get; }
+
+ /// <summary>
/// Is metadata fetcher enabled.
/// </summary>
/// <param name="baseItem">The base item.</param>
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index 9a9d22d33..ddae7dbd3 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Channels
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>ChannelFeatures.</returns>
- ChannelFeatures GetChannelFeatures(string id);
+ ChannelFeatures GetChannelFeatures(Guid? id);
/// <summary>
/// Gets all channel features.
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 1b25fbdbb..d8fad3bfb 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1385,7 +1385,6 @@ namespace MediaBrowser.Controller.Entities
new List<FileSystemMetadata>();
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
- await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh
if (ownedItemsChanged)
{
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 1e3825c6e..23f4c00c1 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using System.Threading.Tasks.Dataflow;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Progress;
@@ -328,11 +329,11 @@ namespace MediaBrowser.Controller.Entities
return;
}
- progress.Report(5);
+ progress.Report(ProgressHelpers.RetrievedChildren);
if (recursive)
{
- ProviderManager.OnRefreshProgress(this, 5);
+ ProviderManager.OnRefreshProgress(this, ProgressHelpers.RetrievedChildren);
}
// Build a dictionary of the current children we have now by Id so we can compare quickly and easily
@@ -353,11 +354,6 @@ namespace MediaBrowser.Controller.Entities
{
await currentChild.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
}
- else
- {
- // metadata is up-to-date; make sure DB has correct images dimensions and hash
- await LibraryManager.UpdateImagesAsync(currentChild).ConfigureAwait(false);
- }
continue;
}
@@ -393,11 +389,11 @@ namespace MediaBrowser.Controller.Entities
validChildrenNeedGeneration = true;
}
- progress.Report(10);
+ progress.Report(ProgressHelpers.UpdatedChildItems);
if (recursive)
{
- ProviderManager.OnRefreshProgress(this, 10);
+ ProviderManager.OnRefreshProgress(this, ProgressHelpers.UpdatedChildItems);
}
cancellationToken.ThrowIfCancellationRequested();
@@ -407,11 +403,13 @@ namespace MediaBrowser.Controller.Entities
var innerProgress = new ActionableProgress<double>();
var folder = this;
- innerProgress.RegisterAction(p =>
+ innerProgress.RegisterAction(innerPercent =>
{
- double newPct = 0.80 * p + 10;
- progress.Report(newPct);
- ProviderManager.OnRefreshProgress(folder, newPct);
+ var percent = ProgressHelpers.GetProgress(ProgressHelpers.UpdatedChildItems, ProgressHelpers.ScannedSubfolders, innerPercent);
+
+ progress.Report(percent);
+
+ ProviderManager.OnRefreshProgress(folder, percent);
});
if (validChildrenNeedGeneration)
@@ -425,11 +423,11 @@ namespace MediaBrowser.Controller.Entities
if (refreshChildMetadata)
{
- progress.Report(90);
+ progress.Report(ProgressHelpers.ScannedSubfolders);
if (recursive)
{
- ProviderManager.OnRefreshProgress(this, 90);
+ ProviderManager.OnRefreshProgress(this, ProgressHelpers.ScannedSubfolders);
}
var container = this as IMetadataContainer;
@@ -437,13 +435,15 @@ namespace MediaBrowser.Controller.Entities
var innerProgress = new ActionableProgress<double>();
var folder = this;
- innerProgress.RegisterAction(p =>
+ innerProgress.RegisterAction(innerPercent =>
{
- double newPct = 0.10 * p + 90;
- progress.Report(newPct);
+ var percent = ProgressHelpers.GetProgress(ProgressHelpers.ScannedSubfolders, ProgressHelpers.RefreshedMetadata, innerPercent);
+
+ progress.Report(percent);
+
if (recursive)
{
- ProviderManager.OnRefreshProgress(folder, newPct);
+ ProviderManager.OnRefreshProgress(folder, percent);
}
});
@@ -458,55 +458,35 @@ namespace MediaBrowser.Controller.Entities
validChildren = Children.ToList();
}
- await RefreshMetadataRecursive(validChildren, refreshOptions, recursive, innerProgress, cancellationToken);
+ await RefreshMetadataRecursive(validChildren, refreshOptions, recursive, innerProgress, cancellationToken).ConfigureAwait(false);
}
}
}
- private async Task RefreshMetadataRecursive(List<BaseItem> children, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
+ private Task RefreshMetadataRecursive(IList<BaseItem> children, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
{
- var numComplete = 0;
- var count = children.Count;
- double currentPercent = 0;
-
- foreach (var child in children)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var innerProgress = new ActionableProgress<double>();
-
- // Avoid implicitly captured closure
- var currentInnerPercent = currentPercent;
-
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = currentInnerPercent;
- innerPercent += p / count;
- progress.Report(innerPercent);
- });
-
- await RefreshChildMetadata(child, refreshOptions, recursive && child.IsFolder, innerProgress, cancellationToken)
- .ConfigureAwait(false);
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
- currentPercent = percent;
-
- progress.Report(percent);
- }
+ return RunTasks(
+ (baseItem, innerProgress) => RefreshChildMetadata(baseItem, refreshOptions, recursive && baseItem.IsFolder, innerProgress, cancellationToken),
+ children,
+ progress,
+ cancellationToken);
}
private async Task RefreshAllMetadataForContainer(IMetadataContainer container, MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
{
- var series = container as Series;
- if (series != null)
- {
- await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- }
+ // limit the amount of concurrent metadata refreshes
+ await ProviderManager.RunMetadataRefresh(
+ async () =>
+ {
+ var series = container as Series;
+ if (series != null)
+ {
+ await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+ }
- await container.RefreshAllMetadata(refreshOptions, progress, cancellationToken).ConfigureAwait(false);
+ await container.RefreshAllMetadata(refreshOptions, progress, cancellationToken).ConfigureAwait(false);
+ },
+ cancellationToken).ConfigureAwait(false);
}
private async Task RefreshChildMetadata(BaseItem child, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
@@ -521,12 +501,15 @@ namespace MediaBrowser.Controller.Entities
{
if (refreshOptions.RefreshItem(child))
{
- await child.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+ // limit the amount of concurrent metadata refreshes
+ await ProviderManager.RunMetadataRefresh(
+ async () => await child.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false),
+ cancellationToken).ConfigureAwait(false);
}
if (recursive && child is Folder folder)
{
- await folder.RefreshMetadataRecursive(folder.Children.ToList(), refreshOptions, true, progress, cancellationToken);
+ await folder.RefreshMetadataRecursive(folder.Children.ToList(), refreshOptions, true, progress, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -539,39 +522,72 @@ namespace MediaBrowser.Controller.Entities
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- private async Task ValidateSubFolders(IList<Folder> children, IDirectoryService directoryService, IProgress<double> progress, CancellationToken cancellationToken)
+ private Task ValidateSubFolders(IList<Folder> children, IDirectoryService directoryService, IProgress<double> progress, CancellationToken cancellationToken)
{
- var numComplete = 0;
- var count = children.Count;
- double currentPercent = 0;
+ return RunTasks(
+ (folder, innerProgress) => folder.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService),
+ children,
+ progress,
+ cancellationToken);
+ }
- foreach (var child in children)
+ /// <summary>
+ /// Runs an action block on a list of children.
+ /// </summary>
+ /// <param name="task">The task to run for each child.</param>
+ /// <param name="children">The list of children.</param>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ private async Task RunTasks<T>(Func<T, IProgress<double>, Task> task, IList<T> children, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var childrenCount = children.Count;
+ var childrenProgress = new double[childrenCount];
+
+ void UpdateProgress()
{
- cancellationToken.ThrowIfCancellationRequested();
+ progress.Report(childrenProgress.Average());
+ }
- var innerProgress = new ActionableProgress<double>();
+ var fanoutConcurrency = ConfigurationManager.Configuration.LibraryScanFanoutConcurrency;
+ var parallelism = fanoutConcurrency == 0 ? Environment.ProcessorCount : fanoutConcurrency;
- // Avoid implicitly captured closure
- var currentInnerPercent = currentPercent;
+ var actionBlock = new ActionBlock<int>(
+ async i =>
+ {
+ var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p =>
+ innerProgress.RegisterAction(innerPercent =>
+ {
+ // round the percent and only update progress if it changed to prevent excessive UpdateProgress calls
+ var innerPercentRounded = Math.Round(innerPercent);
+ if (childrenProgress[i] != innerPercentRounded)
+ {
+ childrenProgress[i] = innerPercentRounded;
+ UpdateProgress();
+ }
+ });
+
+ await task(children[i], innerProgress).ConfigureAwait(false);
+
+ childrenProgress[i] = 100;
+
+ UpdateProgress();
+ },
+ new ExecutionDataflowBlockOptions
{
- double innerPercent = currentInnerPercent;
- innerPercent += p / count;
- progress.Report(innerPercent);
+ MaxDegreeOfParallelism = parallelism,
+ CancellationToken = cancellationToken,
});
- await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService)
- .ConfigureAwait(false);
+ for (var i = 0; i < childrenCount; i++)
+ {
+ actionBlock.Post(i);
+ }
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
- currentPercent = percent;
+ actionBlock.Complete();
- progress.Report(percent);
- }
+ await actionBlock.Completion.ConfigureAwait(false);
}
/// <summary>
@@ -1067,12 +1083,12 @@ namespace MediaBrowser.Controller.Entities
return false;
}
- if (request.Genres.Length > 0)
+ if (request.Genres.Count > 0)
{
return false;
}
- if (request.GenreIds.Length > 0)
+ if (request.GenreIds.Count > 0)
{
return false;
}
@@ -1177,7 +1193,7 @@ namespace MediaBrowser.Controller.Entities
return false;
}
- if (request.GenreIds.Length > 0)
+ if (request.GenreIds.Count > 0)
{
return false;
}
@@ -1768,5 +1784,45 @@ namespace MediaBrowser.Controller.Entities
}
}
}
+
+ /// <summary>
+ /// Contains constants used when reporting scan progress.
+ /// </summary>
+ private static class ProgressHelpers
+ {
+ /// <summary>
+ /// Reported after the folders immediate children are retrieved.
+ /// </summary>
+ public const int RetrievedChildren = 5;
+
+ /// <summary>
+ /// Reported after add, updating, or deleting child items from the LibraryManager.
+ /// </summary>
+ public const int UpdatedChildItems = 10;
+
+ /// <summary>
+ /// Reported once subfolders are scanned.
+ /// When scanning subfolders, the progress will be between [UpdatedItems, ScannedSubfolders].
+ /// </summary>
+ public const int ScannedSubfolders = 50;
+
+ /// <summary>
+ /// Reported once metadata is refreshed.
+ /// When refreshing metadata, the progress will be between [ScannedSubfolders, MetadataRefreshed].
+ /// </summary>
+ public const int RefreshedMetadata = 100;
+
+ /// <summary>
+ /// Gets the current progress given the previous step, next step, and progress in between.
+ /// </summary>
+ /// <param name="previousProgressStep">The previous progress step.</param>
+ /// <param name="nextProgressStep">The next progress step.</param>
+ /// <param name="currentProgress">The current progress step.</param>
+ /// <returns>The progress.</returns>
+ public static double GetProgress(int previousProgressStep, int nextProgressStep, double currentProgress)
+ {
+ return previousProgressStep + ((nextProgressStep - previousProgressStep) * (currentProgress / 100));
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 904752a22..270217356 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -46,7 +46,7 @@ namespace MediaBrowser.Controller.Entities
public string[] ExcludeInheritedTags { get; set; }
- public string[] Genres { get; set; }
+ public IReadOnlyList<string> Genres { get; set; }
public bool? IsSpecialSeason { get; set; }
@@ -116,7 +116,7 @@ namespace MediaBrowser.Controller.Entities
public Guid[] StudioIds { get; set; }
- public Guid[] GenreIds { get; set; }
+ public IReadOnlyList<Guid> GenreIds { get; set; }
public ImageType[] ImageTypes { get; set; }
@@ -162,7 +162,7 @@ namespace MediaBrowser.Controller.Entities
public double? MinCommunityRating { get; set; }
- public Guid[] ChannelIds { get; set; }
+ public IReadOnlyList<Guid> ChannelIds { get; set; }
public int? ParentIndexNumber { get; set; }
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index a262fee15..4e33a6bbd 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -791,7 +791,7 @@ namespace MediaBrowser.Controller.Entities
}
// Apply genre filter
- if (query.Genres.Length > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))
+ if (query.Genres.Count > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))
{
return false;
}
@@ -822,7 +822,7 @@ namespace MediaBrowser.Controller.Entities
}
// Apply genre filter
- if (query.GenreIds.Length > 0 && !query.GenreIds.Any(id =>
+ if (query.GenreIds.Count > 0 && !query.GenreIds.Any(id =>
{
var genreItem = libraryManager.GetItemById(id);
return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase);
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 07f381881..6320b01b8 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -143,26 +143,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The video3 D format.</value>
public Video3DFormat? Video3DFormat { get; set; }
- public string[] GetPlayableStreamFileNames()
- {
- var videoType = VideoType;
-
- if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.BluRay)
- {
- videoType = VideoType.BluRay;
- }
- else if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.Dvd)
- {
- videoType = VideoType.Dvd;
- }
- else
- {
- return Array.Empty<string>();
- }
-
- throw new NotImplementedException();
- }
-
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
@@ -415,31 +395,6 @@ namespace MediaBrowser.Controller.Entities
return updateType;
}
- public static string[] QueryPlayableStreamFiles(string rootPath, VideoType videoType)
- {
- if (videoType == VideoType.Dvd)
- {
- return FileSystem.GetFiles(rootPath, new[] { ".vob" }, false, true)
- .OrderByDescending(i => i.Length)
- .ThenBy(i => i.FullName)
- .Take(1)
- .Select(i => i.FullName)
- .ToArray();
- }
-
- if (videoType == VideoType.BluRay)
- {
- return FileSystem.GetFiles(rootPath, new[] { ".m2ts" }, false, true)
- .OrderByDescending(i => i.Length)
- .ThenBy(i => i.FullName)
- .Take(1)
- .Select(i => i.FullName)
- .ToArray();
- }
-
- return Array.Empty<string>();
- }
-
/// <summary>
/// Gets a value indicating whether [is3 D].
/// </summary>
diff --git a/MediaBrowser.Controller/IResourceFileManager.cs b/MediaBrowser.Controller/IResourceFileManager.cs
deleted file mode 100644
index 26f0424b7..000000000
--- a/MediaBrowser.Controller/IResourceFileManager.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Controller
-{
- public interface IResourceFileManager
- {
- string GetResourcePath(string basePath, string virtualPath);
- }
-}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index ffbb147b0..2456da826 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.System;
+using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller
{
@@ -56,41 +57,42 @@ namespace MediaBrowser.Controller
/// <summary>
/// Gets the system info.
/// </summary>
- /// <param name="cancellationToken">A cancellation token that can be used to cancel the task.</param>
+ /// <param name="source">The originator of the request.</param>
/// <returns>SystemInfo.</returns>
- Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken = default);
+ SystemInfo GetSystemInfo(IPAddress source);
- Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken = default);
+ PublicSystemInfo GetPublicSystemInfo(IPAddress address);
/// <summary>
- /// Gets all the local IP addresses of this API instance. Each address is validated by sending a 'ping' request
- /// to the API that should exist at the address.
+ /// Gets a URL specific for the request.
/// </summary>
- /// <param name="cancellationToken">A cancellation token that can be used to cancel the task.</param>
- /// <returns>A list containing all the local IP addresses of the server.</returns>
- Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken = default);
+ /// <param name="request">The <see cref="HttpRequest"/> instance.</param>
+ /// <param name="port">Optional port number.</param>
+ /// <returns>An accessible URL.</returns>
+ string GetSmartApiUrl(HttpRequest request, int? port = null);
/// <summary>
- /// Gets a local (LAN) URL that can be used to access the API. The hostname used is the first valid configured
- /// IP address that can be found via <see cref="GetLocalIpAddresses"/>. HTTPS will be preferred when available.
+ /// Gets a URL specific for the request.
/// </summary>
- /// <param name="cancellationToken">A cancellation token that can be used to cancel the task.</param>
- /// <returns>The server URL.</returns>
- Task<string> GetLocalApiUrl(CancellationToken cancellationToken = default);
+ /// <param name="remoteAddr">The remote <see cref="IPAddress"/> of the connection.</param>
+ /// <param name="port">Optional port number.</param>
+ /// <returns>An accessible URL.</returns>
+ string GetSmartApiUrl(IPAddress remoteAddr, int? port = null);
/// <summary>
- /// Gets a localhost URL that can be used to access the API using the loop-back IP address (127.0.0.1)
- /// over HTTP (not HTTPS).
+ /// Gets a URL specific for the request.
/// </summary>
- /// <returns>The API URL.</returns>
- string GetLoopbackHttpApiUrl();
+ /// <param name="hostname">The hostname used in the connection.</param>
+ /// <param name="port">Optional port number.</param>
+ /// <returns>An accessible URL.</returns>
+ string GetSmartApiUrl(string hostname, int? port = null);
/// <summary>
- /// Gets a local (LAN) URL that can be used to access the API. HTTPS will be preferred when available.
+ /// Gets a localhost URL that can be used to access the API using the loop-back IP address.
+ /// over HTTP (not HTTPS).
/// </summary>
- /// <param name="address">The IP address to use as the hostname in the URL.</param>
/// <returns>The API URL.</returns>
- string GetLocalApiUrl(IPAddress address);
+ string GetLoopbackHttpApiUrl();
/// <summary>
/// Gets a local (LAN) URL that can be used to access the API.
@@ -106,7 +108,7 @@ namespace MediaBrowser.Controller
/// preferring the HTTPS port, if available.
/// </param>
/// <returns>The API URL.</returns>
- string GetLocalApiUrl(ReadOnlySpan<char> hostname, string scheme = null, int? port = null);
+ string GetLocalApiUrl(string hostname, string scheme = null, int? port = null);
/// <summary>
/// Open a URL in an external browser window.
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index c7c79df76..24b101694 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -571,6 +571,10 @@ namespace MediaBrowser.Controller.Library
string videoPath,
string[] files);
+ void RunMetadataSavers(IReadOnlyList<BaseItem> items, ItemUpdateType updateReason);
+
BaseItem GetParentItem(string parentId, Guid? userId);
+
+ BaseItem GetParentItem(Guid? parentId, Guid? userId);
}
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 9acc98dce..5f75df54e 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -17,6 +17,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
+ <PackageReference Include="System.Threading.Tasks.Dataflow" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 9a6f1231f..91a03e647 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -112,6 +112,16 @@ namespace MediaBrowser.Controller.MediaEncoding
return _mediaEncoder.SupportsHwaccel("vaapi");
}
+ private bool IsTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
+ {
+ var videoStream = state.VideoStream;
+ return IsColorDepth10(state)
+ && _mediaEncoder.SupportsHwaccel("opencl")
+ && options.EnableTonemapping
+ && !string.IsNullOrEmpty(videoStream.VideoRange)
+ && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase);
+ }
+
/// <summary>
/// Gets the name of the output video codec.
/// </summary>
@@ -380,25 +390,9 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetInputPathArgument(EncodingJobInfo state)
{
- var protocol = state.InputProtocol;
var mediaPath = state.MediaPath ?? string.Empty;
- string[] inputPath;
- if (state.IsInputVideo
- && !(state.VideoType == VideoType.Iso && state.IsoMount == null))
- {
- inputPath = MediaEncoderHelpers.GetInputArgument(
- _fileSystem,
- mediaPath,
- state.IsoMount,
- state.PlayableStreamFileNames);
- }
- else
- {
- inputPath = new[] { mediaPath };
- }
-
- return _mediaEncoder.GetInputArgument(inputPath, protocol);
+ return _mediaEncoder.GetInputArgument(mediaPath, state.MediaSource);
}
/// <summary>
@@ -468,6 +462,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+ var isTonemappingSupported = IsTonemappingSupported(state, encodingOptions);
if (!IsCopyCodec(outputVideoCodec))
{
@@ -477,10 +472,23 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isVaapiDecoder)
{
- arg.Append("-hwaccel_output_format vaapi ")
- .Append("-vaapi_device ")
- .Append(encodingOptions.VaapiDevice)
- .Append(' ');
+ if (isTonemappingSupported)
+ {
+ 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 ");
+ }
+ else
+ {
+ arg.Append("-hwaccel_output_format vaapi ")
+ .Append("-vaapi_device ")
+ .Append(encodingOptions.VaapiDevice)
+ .Append(' ');
+ }
}
else if (!isVaapiDecoder && isVaapiEncoder)
{
@@ -529,13 +537,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder)
|| (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder))
{
- var isColorDepth10 = IsColorDepth10(state);
-
- if (isColorDepth10
- && _mediaEncoder.SupportsHwaccel("opencl")
- && encodingOptions.EnableTonemapping
- && !string.IsNullOrEmpty(state.VideoStream.VideoRange)
- && state.VideoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ if (isTonemappingSupported)
{
arg.Append("-init_hw_device opencl=ocl:")
.Append(encodingOptions.OpenclDevice)
@@ -1866,6 +1868,19 @@ namespace MediaBrowser.Controller.MediaEncoding
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isVaapiHevcEncoder = outputVideoCodec.IndexOf("hevc_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
+
+ // Tonemapping and burn-in graphical subtitles requires overlay_vaapi.
+ // But it's still in ffmpeg mailing list. Disable it for now.
+ if (isTonemappingSupported && isTonemappingSupportedOnVaapi)
+ {
+ return GetOutputSizeParam(state, options, outputVideoCodec);
+ }
+
// Setup subtitle scaling
if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
{
@@ -1997,6 +2012,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public List<string> GetScalingFilters(
EncodingJobInfo state,
+ EncodingOptions options,
int? videoWidth,
int? videoHeight,
Video3DFormat? threedFormat,
@@ -2035,6 +2051,19 @@ namespace MediaBrowser.Controller.MediaEncoding
|| state.DeInterlace("h265", true)
|| state.DeInterlace("hevc", true);
+ var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && !qsv_or_vaapi;
+
+ var outputPixFmt = string.Empty;
+ if (isTonemappingSupported && isTonemappingSupportedOnVaapi)
+ {
+ outputPixFmt = "format=p010:out_range=limited";
+ }
+ else
+ {
+ outputPixFmt = "format=nv12";
+ }
+
if (!videoWidth.HasValue
|| outputWidth != videoWidth.Value
|| !videoHeight.HasValue
@@ -2045,10 +2074,11 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
- "{0}=w={1}:h={2}:format=nv12{3}",
+ "{0}=w={1}:h={2}{3}{4}",
qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi",
outputWidth,
outputHeight,
+ ":" + outputPixFmt,
(qsv_or_vaapi && isDeintEnabled) ? ":deinterlace=1" : string.Empty));
}
else
@@ -2056,8 +2086,9 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
- "{0}=format=nv12{1}",
+ "{0}={1}{2}",
qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi",
+ outputPixFmt,
(qsv_or_vaapi && isDeintEnabled) ? ":deinterlace=1" : string.Empty));
}
}
@@ -2290,6 +2321,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isSwDecoder = string.IsNullOrEmpty(videoDecoder);
var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
+ var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isVaapiHevcEncoder = outputVideoCodec.IndexOf("hevc_vaapi", StringComparison.OrdinalIgnoreCase) != -1;
var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1;
@@ -2300,6 +2332,10 @@ namespace MediaBrowser.Controller.MediaEncoding
var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1;
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
var isColorDepth10 = IsColorDepth10(state);
+ var isTonemappingSupported = IsTonemappingSupported(state, options);
+ var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || 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 hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -2311,18 +2347,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
- if ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder)
- || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder))
+ if (isTonemappingSupportedOnNvenc || isTonemappingSupportedOnAmf || isTonemappingSupportedOnVaapi)
{
// 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.
- if (isColorDepth10
- && _mediaEncoder.SupportsHwaccel("opencl")
- && options.EnableTonemapping
- && !string.IsNullOrEmpty(videoStream.VideoRange)
- && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase))
+ // Intel Kaby Lake or newer is required.
+ if (isTonemappingSupported)
{
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
@@ -2355,10 +2387,35 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.Add("format=p010");
}
- // Upload the HDR10 or HLG data to the OpenCL device,
- // use tonemap_opencl filter for tone mapping,
- // and then download the SDR data to memory.
- filters.Add("hwupload");
+ if (isNvdecHevcDecoder || isSwDecoder || isD3d11vaDecoder)
+ {
+ // Upload the HDR10 or HLG data to the OpenCL device,
+ // use tonemap_opencl filter for tone mapping,
+ // and then download the SDR data to memory.
+ filters.Add("hwupload");
+ }
+
+ if (isVaapiDecoder)
+ {
+ isScalingInAdvance = true;
+ filters.AddRange(
+ GetScalingFilters(
+ state,
+ options,
+ inputWidth,
+ inputHeight,
+ threeDFormat,
+ videoDecoder,
+ outputVideoCodec,
+ request.Width,
+ request.Height,
+ request.MaxWidth,
+ request.MaxHeight));
+
+ // hwmap the HDR data to opencl device by cl-va p010 interop.
+ filters.Add("hwmap");
+ }
+
filters.Add(
string.Format(
CultureInfo.InvariantCulture,
@@ -2369,33 +2426,46 @@ namespace MediaBrowser.Controller.MediaEncoding
options.TonemappingPeak,
options.TonemappingParam,
options.TonemappingRange));
- filters.Add("hwdownload");
- if (isLibX264Encoder
- || isLibX265Encoder
- || hasGraphicalSubs
- || (isNvdecHevcDecoder && isDeinterlaceHevc)
- || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc))
+ if (isNvdecHevcDecoder || isSwDecoder || isD3d11vaDecoder)
{
- filters.Add("format=nv12");
+ filters.Add("hwdownload");
+ }
+
+ if (isSwDecoder || isD3d11vaDecoder)
+ {
+ if (isLibX264Encoder
+ || isLibX265Encoder
+ || hasGraphicalSubs
+ || (isNvdecHevcDecoder && isDeinterlaceHevc)
+ || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc))
+ {
+ filters.Add("format=nv12");
+ }
+ }
+
+ if (isVaapiDecoder)
+ {
+ // Reverse the data route from opencl to vaapi.
+ filters.Add("hwmap=derive_device=vaapi:reverse=1");
}
}
}
- // When the input may or may not be hardware VAAPI decodable
- if (isVaapiH264Encoder || isVaapiHevcEncoder)
+ // When the input may or may not be hardware VAAPI decodable.
+ if ((isVaapiH264Encoder || isVaapiHevcEncoder) && !isTonemappingSupported && !isTonemappingSupportedOnVaapi)
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
}
- // When burning in graphical subtitles using overlay_qsv, upload videostream to the same qsv context
+ // When burning in graphical subtitles using overlay_qsv, upload videostream to the same qsv context.
else if (isLinux && hasGraphicalSubs && (isQsvH264Encoder || isQsvHevcEncoder))
{
filters.Add("hwupload=extra_hw_frames=64");
}
- // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
+ // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first.
else if (IsVaapiSupported(state) && isVaapiDecoder && (isLibX264Encoder || isLibX265Encoder))
{
var codec = videoStream.Codec.ToLowerInvariant();
@@ -2422,7 +2492,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- // Add hardware deinterlace filter before scaling filter
+ // Add hardware deinterlace filter before scaling filter.
if (isDeinterlaceH264)
{
if (isVaapiH264Encoder)
@@ -2435,7 +2505,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- // Add software deinterlace filter before scaling filter
+ // Add software deinterlace filter before scaling filter.
if ((isDeinterlaceH264 || isDeinterlaceHevc)
&& !isVaapiH264Encoder
&& !isVaapiHevcEncoder
@@ -2467,6 +2537,7 @@ namespace MediaBrowser.Controller.MediaEncoding
filters.AddRange(
GetScalingFilters(
state,
+ options,
inputWidth,
inputHeight,
threeDFormat,
@@ -2483,6 +2554,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasTextSubs)
{
+ // Convert hw context from ocl to va.
+ // For tonemapping and text subs burn-in.
+ if (isTonemappingSupported && isTonemappingSupportedOnVaapi)
+ {
+ filters.Add("scale_vaapi");
+ }
+
// Test passed on Intel and AMD gfx
filters.Add("hwmap=mode=read+write");
filters.Add("format=nv12");
@@ -2579,18 +2657,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- public string GetProbeSizeArgument(int numInputFiles)
- => numInputFiles > 1 ? "-probesize " + _configuration.GetFFmpegProbeSize() : string.Empty;
-
- public string GetAnalyzeDurationArgument(int numInputFiles)
- => numInputFiles > 1 ? "-analyzeduration " + _configuration.GetFFmpegAnalyzeDuration() : string.Empty;
-
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions)
{
var inputModifier = string.Empty;
-
- var numInputFiles = state.PlayableStreamFileNames.Length > 0 ? state.PlayableStreamFileNames.Length : 1;
- var probeSizeArgument = GetProbeSizeArgument(numInputFiles);
+ var probeSizeArgument = string.Empty;
string analyzeDurationArgument;
if (state.MediaSource.AnalyzeDurationMs.HasValue)
@@ -2599,7 +2669,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else
{
- analyzeDurationArgument = GetAnalyzeDurationArgument(numInputFiles);
+ analyzeDurationArgument = string.Empty;
}
if (!string.IsNullOrEmpty(probeSizeArgument))
@@ -2783,32 +2853,6 @@ namespace MediaBrowser.Controller.MediaEncoding
state.IsoType = mediaSource.IsoType;
- if (mediaSource.VideoType.HasValue)
- {
- state.VideoType = mediaSource.VideoType.Value;
-
- if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, mediaSource.VideoType.Value).Select(Path.GetFileName).ToArray();
- }
- else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.BluRay)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.BluRay).Select(Path.GetFileName).ToArray();
- }
- else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.Dvd)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.Dvd).Select(Path.GetFileName).ToArray();
- }
- else
- {
- state.PlayableStreamFileNames = Array.Empty<string>();
- }
- }
- else
- {
- state.PlayableStreamFileNames = Array.Empty<string>();
- }
-
if (mediaSource.Timestamp.HasValue)
{
state.InputTimestamp = mediaSource.Timestamp.Value;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 52794a69b..dacd6dea6 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -33,10 +33,6 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool IsInputVideo { get; set; }
- public IIsoMount IsoMount { get; set; }
-
- public string[] PlayableStreamFileNames { get; set; }
-
public string OutputAudioCodec { get; set; }
public int? OutputVideoBitrate { get; set; }
@@ -313,7 +309,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
TranscodingType = jobType;
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- PlayableStreamFileNames = Array.Empty<string>();
SupportedAudioCodecs = Array.Empty<string>();
SupportedVideoCodecs = Array.Empty<string>();
SupportedSubtitleCodecs = Array.Empty<string>();
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index f6bc1f4de..e7f042d2f 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -61,18 +62,18 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Extracts the video image.
/// </summary>
- Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
+ Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
- Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
+ Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
/// <summary>
/// Extracts the video images on interval.
/// </summary>
Task ExtractVideoImagesOnInterval(
- string[] inputFiles,
+ string inputFile,
string container,
MediaStream videoStream,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
Video3DFormat? threedFormat,
TimeSpan interval,
string targetDirectory,
@@ -91,10 +92,10 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the input argument.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
+ /// <param name="inputFile">The input file.</param>
+ /// <param name="mediaSource">The mediaSource.</param>
/// <returns>System.String.</returns>
- string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol);
+ string GetInputArgument(string inputFile, MediaSourceInfo mediaSource);
/// <summary>
/// Gets the time parameter.
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
index ce53c23ad..281d50372 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
@@ -13,38 +13,5 @@ namespace MediaBrowser.Controller.MediaEncoding
/// </summary>
public static class MediaEncoderHelpers
{
- /// <summary>
- /// Gets the input argument.
- /// </summary>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="videoPath">The video path.</param>
- /// <param name="isoMount">The iso mount.</param>
- /// <param name="playableStreamFileNames">The playable stream file names.</param>
- /// <returns>string[].</returns>
- public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, IIsoMount isoMount, IReadOnlyCollection<string> playableStreamFileNames)
- {
- if (playableStreamFileNames.Count > 0)
- {
- if (isoMount == null)
- {
- return GetPlayableStreamFiles(fileSystem, videoPath, playableStreamFileNames);
- }
-
- return GetPlayableStreamFiles(fileSystem, isoMount.MountedPath, playableStreamFileNames);
- }
-
- return new[] { videoPath };
- }
-
- private static string[] GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames)
- {
- var allFiles = fileSystem
- .GetFilePaths(rootPath, true)
- .ToArray();
-
- return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
- .Where(f => !string.IsNullOrEmpty(f))
- .ToArray();
- }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
index 59729de49..2cb04bdc4 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
@@ -1,9 +1,7 @@
#pragma warning disable CS1591
-using System;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.MediaEncoding
{
@@ -14,14 +12,5 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool ExtractChapters { get; set; }
public DlnaProfileType MediaType { get; set; }
-
- public IIsoMount MountedIso { get; set; }
-
- public string[] PlayableStreamFileNames { get; set; }
-
- public MediaInfoRequest()
- {
- PlayableStreamFileNames = Array.Empty<string>();
- }
}
}
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
index 0194c596f..93573e08e 100644
--- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs
+++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
@@ -58,5 +58,10 @@ namespace MediaBrowser.Controller.Net
/// Gets or sets a value indicating whether the token is authenticated.
/// </summary>
public bool IsAuthenticated { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the request has a token.
+ /// </summary>
+ public bool HasToken { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
index fbf2c5213..f6c592070 100644
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
@@ -31,7 +31,7 @@ namespace MediaBrowser.Controller.Playlists
/// <param name="itemIds">The item ids.</param>
/// <param name="userId">The user identifier.</param>
/// <returns>Task.</returns>
- Task AddToPlaylistAsync(Guid playlistId, ICollection<Guid> itemIds, Guid userId);
+ Task AddToPlaylistAsync(Guid playlistId, IReadOnlyCollection<Guid> itemIds, Guid userId);
/// <summary>
/// Removes from playlist.
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 996ec27c0..0a4967223 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -46,6 +46,14 @@ namespace MediaBrowser.Controller.Providers
Task<ItemUpdateType> RefreshSingleItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken);
/// <summary>
+ /// Runs multiple metadata refreshes concurrently.
+ /// </summary>
+ /// <param name="action">The action to run.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
+ Task RunMetadataRefresh(Func<Task> action, CancellationToken cancellationToken);
+
+ /// <summary>
/// Saves the image.
/// </summary>
/// <param name="item">The item.</param>
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index ce58a60b9..d09852870 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
@@ -54,7 +55,7 @@ namespace MediaBrowser.Controller.Session
/// Gets or sets the playable media types.
/// </summary>
/// <value>The playable media types.</value>
- public string[] PlayableMediaTypes
+ public IReadOnlyList<string> PlayableMediaTypes
{
get
{
@@ -230,7 +231,7 @@ namespace MediaBrowser.Controller.Session
/// Gets or sets the supported commands.
/// </summary>
/// <value>The supported commands.</value>
- public GeneralCommandType[] SupportedCommands
+ public IReadOnlyList<GeneralCommandType> SupportedCommands
=> Capabilities == null ? Array.Empty<GeneralCommandType>() : Capabilities.SupportedCommands;
public Tuple<ISessionController, bool> EnsureController<T>(Func<SessionInfo, ISessionController> factory)