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