jellyfin/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs

303 lines
12 KiB
C#
Raw Normal View History

#nullable disable
using System;
2019-01-26 22:31:59 +01:00
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Naming.Common;
2014-06-09 21:16:14 +02:00
using MediaBrowser.Controller.Chapters;
2014-05-07 04:28:19 +02:00
using MediaBrowser.Controller.Configuration;
2014-02-09 22:11:11 +01:00
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Lyrics;
2014-02-20 17:37:41 +01:00
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
2014-05-07 04:28:19 +02:00
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
2022-03-01 00:16:25 +01:00
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.MediaInfo
{
2022-03-31 16:17:37 +02:00
/// <summary>
/// The probe provider.
/// </summary>
2022-03-29 17:06:30 +02:00
public class ProbeProvider : ICustomMetadataProvider<Episode>,
ICustomMetadataProvider<MusicVideo>,
ICustomMetadataProvider<Movie>,
2016-05-07 23:01:21 +02:00
ICustomMetadataProvider<Trailer>,
ICustomMetadataProvider<Video>,
ICustomMetadataProvider<Audio>,
2017-01-07 21:52:56 +01:00
ICustomMetadataProvider<AudioBook>,
2014-02-19 06:21:03 +01:00
IHasOrder,
2015-03-05 07:34:36 +01:00
IForcedProvider,
2018-09-12 19:26:21 +02:00
IPreRefreshProvider,
IHasItemChangeMonitor
{
2022-03-29 17:06:30 +02:00
private readonly ILogger<ProbeProvider> _logger;
private readonly AudioResolver _audioResolver;
private readonly SubtitleResolver _subtitleResolver;
private readonly LyricResolver _lyricResolver;
2021-12-02 11:21:59 +01:00
private readonly FFProbeVideoInfo _videoProber;
2022-03-29 17:06:30 +02:00
private readonly AudioFileProber _audioProber;
2020-09-07 13:20:39 +02:00
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
2022-03-31 16:17:37 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="ProbeProvider"/> class.
/// </summary>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
/// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
/// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param>
2022-03-31 16:17:37 +02:00
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param>
/// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="subtitleManager">Instance of the <see cref="ISubtitleManager"/> interface.</param>
/// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/>.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="namingOptions">The <see cref="NamingOptions"/>.</param>
/// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
2022-03-29 17:06:30 +02:00
public ProbeProvider(
2020-09-07 13:20:39 +02:00
IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
IBlurayExaminer blurayExaminer,
2020-09-07 13:20:39 +02:00
ILocalizationManager localization,
IEncodingManager encodingManager,
IServerConfigurationManager config,
ISubtitleManager subtitleManager,
IChapterManager chapterManager,
ILibraryManager libraryManager,
2022-03-01 00:16:25 +01:00
IFileSystem fileSystem,
ILoggerFactory loggerFactory,
NamingOptions namingOptions,
ILyricManager lyricManager)
2020-09-07 13:20:39 +02:00
{
2022-03-29 17:06:30 +02:00
_logger = loggerFactory.CreateLogger<ProbeProvider>();
_audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
_subtitleResolver = new SubtitleResolver(loggerFactory.CreateLogger<SubtitleResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
_lyricResolver = new LyricResolver(loggerFactory.CreateLogger<LyricResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
2021-12-02 11:21:59 +01:00
_videoProber = new FFProbeVideoInfo(
2022-11-27 14:35:07 +01:00
loggerFactory.CreateLogger<FFProbeVideoInfo>(),
2021-12-02 11:21:59 +01:00
mediaSourceManager,
mediaEncoder,
itemRepo,
blurayExaminer,
2021-12-02 11:21:59 +01:00
localization,
encodingManager,
config,
subtitleManager,
chapterManager,
libraryManager,
_audioResolver,
_subtitleResolver);
_audioProber = new AudioFileProber(
loggerFactory.CreateLogger<AudioFileProber>(),
mediaSourceManager,
mediaEncoder,
itemRepo,
libraryManager,
_lyricResolver,
lyricManager);
2020-09-07 13:20:39 +02:00
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
public string Name => "Probe Provider";
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2020-09-07 13:20:39 +02:00
public int Order => 100;
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2018-09-12 19:26:21 +02:00
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
2018-09-12 19:26:21 +02:00
var video = item as Video;
2022-12-05 15:00:20 +01:00
if (video is null || video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Iso)
2018-09-12 19:26:21 +02:00
{
var path = item.Path;
if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol)
{
var file = directoryService.GetFile(path);
2022-12-05 15:01:13 +01:00
if (file is not null && file.LastWriteTimeUtc != item.DateModified)
2018-09-12 19:26:21 +02:00
{
_logger.LogDebug("Refreshing {ItemPath} due to date modified timestamp change.", path);
2018-09-12 19:26:21 +02:00
return true;
}
}
}
if (video is not null
&& item.SupportsLocalMetadata
&& !video.IsPlaceHolder)
2018-09-12 19:26:21 +02:00
{
if (!video.SubtitleFiles.SequenceEqual(
_subtitleResolver.GetExternalFiles(video, directoryService, false)
.Select(info => info.Path).ToList(),
StringComparer.Ordinal))
{
_logger.LogDebug("Refreshing {ItemPath} due to external subtitles change.", item.Path);
return true;
}
if (!video.AudioFiles.SequenceEqual(
_audioResolver.GetExternalFiles(video, directoryService, false)
.Select(info => info.Path).ToList(),
StringComparer.Ordinal))
{
_logger.LogDebug("Refreshing {ItemPath} due to external audio change.", item.Path);
return true;
}
2021-11-22 21:47:52 +01:00
}
if (item is Audio audio
&& item.SupportsLocalMetadata
&& !audio.LyricFiles.SequenceEqual(
_lyricResolver.GetExternalFiles(audio, directoryService, false)
.Select(info => info.Path).ToList(),
StringComparer.Ordinal))
2021-11-22 21:47:52 +01:00
{
_logger.LogDebug("Refreshing {ItemPath} due to external lyrics change.", item.Path);
2021-11-22 21:47:52 +01:00
return true;
2018-09-12 19:26:21 +02:00
}
return false;
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2018-09-12 19:26:21 +02:00
public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
2014-06-09 21:16:14 +02:00
return FetchVideoInfo(item, options, cancellationToken);
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2018-09-12 19:26:21 +02:00
public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
2014-06-09 21:16:14 +02:00
return FetchVideoInfo(item, options, cancellationToken);
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2018-09-12 19:26:21 +02:00
public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
2014-06-09 21:16:14 +02:00
return FetchVideoInfo(item, options, cancellationToken);
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2016-05-07 23:01:21 +02:00
public Task<ItemUpdateType> FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
return FetchVideoInfo(item, options, cancellationToken);
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2014-06-09 21:16:14 +02:00
public Task<ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
2014-06-09 21:16:14 +02:00
return FetchVideoInfo(item, options, cancellationToken);
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2014-06-09 21:16:14 +02:00
public Task<ItemUpdateType> FetchAsync(Audio item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
2018-09-12 19:26:21 +02:00
return FetchAudioInfo(item, options, cancellationToken);
2017-01-07 21:52:56 +01:00
}
2022-03-31 16:17:37 +02:00
/// <inheritdoc />
2017-01-07 21:52:56 +01:00
public Task<ItemUpdateType> FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
2018-09-12 19:26:21 +02:00
return FetchAudioInfo(item, options, cancellationToken);
}
2022-03-31 16:17:37 +02:00
/// <summary>
/// Fetches video information for an item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <typeparam name="T">The type of item to resolve.</typeparam>
/// <returns>A <see cref="Task"/> fetching the <see cref="ItemUpdateType"/> for an item.</returns>
2014-06-09 21:16:14 +02:00
public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
where T : Video
{
2018-09-12 19:26:21 +02:00
if (item.IsPlaceHolder)
{
return _cachedTask;
}
2018-09-12 19:26:21 +02:00
if (!item.IsCompleteMedia)
{
return _cachedTask;
}
2018-09-12 19:26:21 +02:00
if (item.IsVirtualItem)
{
return _cachedTask;
}
if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
2017-09-18 18:52:22 +02:00
{
return _cachedTask;
}
if (item.IsShortcut)
{
FetchShortcutInfo(item);
}
2015-06-29 03:10:45 +02:00
2021-12-02 11:21:59 +01:00
return _videoProber.ProbeVideo(item, options, cancellationToken);
}
2018-09-12 19:26:21 +02:00
private string NormalizeStrmLine(string line)
{
2020-08-07 19:26:28 +02:00
return line.Replace("\t", string.Empty, StringComparison.Ordinal)
.Replace("\r", string.Empty, StringComparison.Ordinal)
.Replace("\n", string.Empty, StringComparison.Ordinal)
2017-11-01 20:50:16 +01:00
.Trim();
}
2018-09-12 19:26:21 +02:00
private void FetchShortcutInfo(BaseItem item)
{
2019-01-26 22:31:59 +01:00
item.ShortcutPath = File.ReadAllLines(item.Path)
2018-09-12 19:26:21 +02:00
.Select(NormalizeStrmLine)
2020-12-02 15:38:52 +01:00
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith('#'));
2018-09-12 19:26:21 +02:00
}
2022-03-31 16:17:37 +02:00
/// <summary>
/// Fetches audio information for an item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <typeparam name="T">The type of item to resolve.</typeparam>
/// <returns>A <see cref="Task"/> fetching the <see cref="ItemUpdateType"/> for an item.</returns>
2018-09-12 19:26:21 +02:00
public Task<ItemUpdateType> FetchAudioInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
where T : Audio
{
2018-09-12 19:26:21 +02:00
if (item.IsVirtualItem)
{
return _cachedTask;
}
if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
{
return _cachedTask;
}
2018-09-12 19:26:21 +02:00
if (item.IsShortcut)
{
FetchShortcutInfo(item);
}
2021-12-02 11:21:59 +01:00
return _audioProber.Probe(item, options, cancellationToken);
}
}
}