|
|
@ -1,11 +1,8 @@
|
|
|
|
#nullable disable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma warning disable CA1068, CS1591
|
|
|
|
#pragma warning disable CA1068, CS1591
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
using System.Linq;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using System.Threading.Tasks;
|
|
|
@ -83,9 +80,9 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
where T : Video
|
|
|
|
where T : Video
|
|
|
|
{
|
|
|
|
{
|
|
|
|
BlurayDiscInfo blurayDiscInfo = null;
|
|
|
|
BlurayDiscInfo? blurayDiscInfo = null;
|
|
|
|
|
|
|
|
|
|
|
|
Model.MediaInfo.MediaInfo mediaInfoResult = null;
|
|
|
|
Model.MediaInfo.MediaInfo? mediaInfoResult = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (!item.IsShortcut || options.EnableRemoteContentProbe)
|
|
|
|
if (!item.IsShortcut || options.EnableRemoteContentProbe)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -131,7 +128,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
var m2ts = _mediaEncoder.GetPrimaryPlaylistM2tsFiles(item.Path);
|
|
|
|
var m2ts = _mediaEncoder.GetPrimaryPlaylistM2tsFiles(item.Path);
|
|
|
|
|
|
|
|
|
|
|
|
// Return if no playable .m2ts files are found
|
|
|
|
// Return if no playable .m2ts files are found
|
|
|
|
if (blurayDiscInfo.Files.Length == 0 || m2ts.Count == 0)
|
|
|
|
if (blurayDiscInfo is null || blurayDiscInfo.Files.Length == 0 || m2ts.Count == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_logger.LogError("No playable .m2ts files found in Blu-ray structure, skipping FFprobe.");
|
|
|
|
_logger.LogError("No playable .m2ts files found in Blu-ray structure, skipping FFprobe.");
|
|
|
|
return ItemUpdateType.MetadataImport;
|
|
|
|
return ItemUpdateType.MetadataImport;
|
|
|
@ -192,16 +189,14 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
protected async Task Fetch(
|
|
|
|
protected async Task Fetch(
|
|
|
|
Video video,
|
|
|
|
Video video,
|
|
|
|
CancellationToken cancellationToken,
|
|
|
|
CancellationToken cancellationToken,
|
|
|
|
Model.MediaInfo.MediaInfo mediaInfo,
|
|
|
|
Model.MediaInfo.MediaInfo? mediaInfo,
|
|
|
|
BlurayDiscInfo blurayInfo,
|
|
|
|
BlurayDiscInfo? blurayInfo,
|
|
|
|
MetadataRefreshOptions options)
|
|
|
|
MetadataRefreshOptions options)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
List<MediaStream> mediaStreams;
|
|
|
|
List<MediaStream> mediaStreams = new List<MediaStream>();
|
|
|
|
IReadOnlyList<MediaAttachment> mediaAttachments;
|
|
|
|
IReadOnlyList<MediaAttachment> mediaAttachments;
|
|
|
|
ChapterInfo[] chapters;
|
|
|
|
ChapterInfo[] chapters;
|
|
|
|
|
|
|
|
|
|
|
|
mediaStreams = new List<MediaStream>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add external streams before adding the streams from the file to preserve stream IDs on remote videos
|
|
|
|
// Add external streams before adding the streams from the file to preserve stream IDs on remote videos
|
|
|
|
await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
|
|
|
await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
|
@ -221,18 +216,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
video.TotalBitrate = mediaInfo.Bitrate;
|
|
|
|
video.TotalBitrate = mediaInfo.Bitrate;
|
|
|
|
video.RunTimeTicks = mediaInfo.RunTimeTicks;
|
|
|
|
video.RunTimeTicks = mediaInfo.RunTimeTicks;
|
|
|
|
video.Size = mediaInfo.Size;
|
|
|
|
video.Size = mediaInfo.Size;
|
|
|
|
|
|
|
|
|
|
|
|
if (video.VideoType == VideoType.VideoFile)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video.Container = extension;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
video.Container = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video.Container = mediaInfo.Container;
|
|
|
|
video.Container = mediaInfo.Container;
|
|
|
|
|
|
|
|
|
|
|
|
chapters = mediaInfo.Chapters ?? Array.Empty<ChapterInfo>();
|
|
|
|
chapters = mediaInfo.Chapters ?? Array.Empty<ChapterInfo>();
|
|
|
@ -243,8 +226,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var currentMediaStreams = video.GetMediaStreams();
|
|
|
|
foreach (var mediaStream in video.GetMediaStreams())
|
|
|
|
foreach (var mediaStream in currentMediaStreams)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!mediaStream.IsExternal)
|
|
|
|
if (!mediaStream.IsExternal)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -295,8 +277,8 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
_itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
|
|
|
|
_itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
|
|
|
|
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh
|
|
|
|
options.MetadataRefreshMode == MetadataRefreshMode.Default)
|
|
|
|
|| options.MetadataRefreshMode == MetadataRefreshMode.Default)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (_config.Configuration.DummyChapterDuration > 0 && chapters.Length == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
|
|
|
|
if (_config.Configuration.DummyChapterDuration > 0 && chapters.Length == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -321,11 +303,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
{
|
|
|
|
{
|
|
|
|
for (int i = 0; i < chapters.Length; i++)
|
|
|
|
for (int i = 0; i < chapters.Length; i++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
string name = chapters[i].Name;
|
|
|
|
string? name = chapters[i].Name;
|
|
|
|
// Check if the name is empty and/or if the name is a time
|
|
|
|
// Check if the name is empty and/or if the name is a time
|
|
|
|
// Some ripping programs do that.
|
|
|
|
// Some ripping programs do that.
|
|
|
|
if (string.IsNullOrWhiteSpace(name) ||
|
|
|
|
if (string.IsNullOrWhiteSpace(name)
|
|
|
|
TimeSpan.TryParse(name, out _))
|
|
|
|
|| TimeSpan.TryParse(name, out _))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
chapters[i].Name = string.Format(
|
|
|
|
chapters[i].Name = string.Format(
|
|
|
|
CultureInfo.InvariantCulture,
|
|
|
|
CultureInfo.InvariantCulture,
|
|
|
@ -384,23 +366,18 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
// Use the ffprobe values if these are empty
|
|
|
|
// Use the ffprobe values if these are empty
|
|
|
|
if (videoStream is not null)
|
|
|
|
if (videoStream is not null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
videoStream.BitRate = IsEmpty(videoStream.BitRate) ? currentBitRate : videoStream.BitRate;
|
|
|
|
videoStream.BitRate = videoStream.BitRate.GetValueOrDefault() == 0 ? currentBitRate : videoStream.BitRate;
|
|
|
|
videoStream.Width = IsEmpty(videoStream.Width) ? currentWidth : videoStream.Width;
|
|
|
|
videoStream.Width = videoStream.Width.GetValueOrDefault() == 0 ? currentWidth : videoStream.Width;
|
|
|
|
videoStream.Height = IsEmpty(videoStream.Height) ? currentHeight : videoStream.Height;
|
|
|
|
videoStream.Height = videoStream.Height.GetValueOrDefault() == 0 ? currentHeight : videoStream.Height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool IsEmpty(int? num)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return !num.HasValue || num.Value == 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets information about the longest playlist on a bdrom.
|
|
|
|
/// Gets information about the longest playlist on a bdrom.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="path">The path.</param>
|
|
|
|
/// <param name="path">The path.</param>
|
|
|
|
/// <returns>VideoStream.</returns>
|
|
|
|
/// <returns>VideoStream.</returns>
|
|
|
|
private BlurayDiscInfo GetBDInfo(string path)
|
|
|
|
private BlurayDiscInfo? GetBDInfo(string path)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ArgumentException.ThrowIfNullOrEmpty(path);
|
|
|
|
ArgumentException.ThrowIfNullOrEmpty(path);
|
|
|
|
|
|
|
|
|
|
|
@ -527,11 +504,14 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
|
|
|
|
|
|
|
|
private void FetchPeople(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
|
|
|
|
private void FetchPeople(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var replaceData = options.ReplaceAllMetadata;
|
|
|
|
if (video.IsLocked
|
|
|
|
|
|
|
|
|| video.LockedFields.Contains(MetadataField.Cast)
|
|
|
|
if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Cast))
|
|
|
|
|| data.People.Length == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (replaceData || _libraryManager.GetPeople(video).Count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (options.ReplaceAllMetadata || _libraryManager.GetPeople(video).Count == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var people = new List<PersonInfo>();
|
|
|
|
var people = new List<PersonInfo>();
|
|
|
|
|
|
|
|
|
|
|
@ -548,12 +528,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
_libraryManager.UpdatePeople(video, people);
|
|
|
|
_libraryManager.UpdatePeople(video, people);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private SubtitleOptions GetOptions()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return _config.GetConfiguration<SubtitleOptions>("subtitles");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Adds the external subtitles.
|
|
|
|
/// Adds the external subtitles.
|
|
|
@ -575,7 +549,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default ||
|
|
|
|
var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default ||
|
|
|
|
options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
|
|
|
|
options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
|
|
|
|
|
|
|
|
|
|
|
|
var subtitleOptions = GetOptions();
|
|
|
|
var subtitleOptions = _config.GetConfiguration<SubtitleOptions>("subtitles");
|
|
|
|
|
|
|
|
|
|
|
|
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
|
|
|
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
|
|
|
|
|
|
|
|
|
|
@ -659,9 +633,9 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="video">The video.</param>
|
|
|
|
/// <param name="video">The video.</param>
|
|
|
|
/// <returns>An array of dummy chapters.</returns>
|
|
|
|
/// <returns>An array of dummy chapters.</returns>
|
|
|
|
private ChapterInfo[] CreateDummyChapters(Video video)
|
|
|
|
internal ChapterInfo[] CreateDummyChapters(Video video)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var runtime = video.RunTimeTicks ?? 0;
|
|
|
|
var runtime = video.RunTimeTicks.GetValueOrDefault();
|
|
|
|
|
|
|
|
|
|
|
|
// Only process files with a runtime higher than 0 and lower than 12h. The latter are likely corrupted.
|
|
|
|
// Only process files with a runtime higher than 0 and lower than 12h. The latter are likely corrupted.
|
|
|
|
if (runtime < 0 || runtime > TimeSpan.FromHours(12).Ticks)
|
|
|
|
if (runtime < 0 || runtime > TimeSpan.FromHours(12).Ticks)
|
|
|
@ -671,12 +645,15 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
CultureInfo.InvariantCulture,
|
|
|
|
CultureInfo.InvariantCulture,
|
|
|
|
"{0} has an invalid runtime of {1} minutes",
|
|
|
|
"{0} has an invalid runtime of {1} minutes",
|
|
|
|
video.Name,
|
|
|
|
video.Name,
|
|
|
|
TimeSpan.FromTicks(runtime).Minutes));
|
|
|
|
TimeSpan.FromTicks(runtime).TotalMinutes));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
long dummyChapterDuration = TimeSpan.FromSeconds(_config.Configuration.DummyChapterDuration).Ticks;
|
|
|
|
long dummyChapterDuration = TimeSpan.FromSeconds(_config.Configuration.DummyChapterDuration).Ticks;
|
|
|
|
if (runtime > dummyChapterDuration)
|
|
|
|
if (runtime <= dummyChapterDuration)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
return Array.Empty<ChapterInfo>();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int chapterCount = (int)(runtime / dummyChapterDuration);
|
|
|
|
int chapterCount = (int)(runtime / dummyChapterDuration);
|
|
|
|
var chapters = new ChapterInfo[chapterCount];
|
|
|
|
var chapters = new ChapterInfo[chapterCount];
|
|
|
|
|
|
|
|
|
|
|
@ -693,8 +670,5 @@ namespace MediaBrowser.Providers.MediaInfo
|
|
|
|
|
|
|
|
|
|
|
|
return chapters;
|
|
|
|
return chapters;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Array.Empty<ChapterInfo>();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|