Merge pull request #9254 from Shadowghost/dvdbdfix

pull/9468/head
Bond-009 1 year ago committed by GitHub
commit 6351d1022b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -80,11 +80,13 @@ using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.SyncPlay;
using MediaBrowser.Controller.TV; using MediaBrowser.Controller.TV;
using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.MediaEncoding.BdInfo;
using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.MediaEncoding.Subtitles;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
@ -529,6 +531,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>(); serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>(); serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
serviceCollection.AddSingleton<IUserDataManager, UserDataManager>(); serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();

@ -70,11 +70,23 @@ namespace Emby.Server.Implementations.Library.Resolvers
{ {
if (IsDvdDirectory(child.FullName, filename, DirectoryService)) if (IsDvdDirectory(child.FullName, filename, DirectoryService))
{ {
videoType = VideoType.Dvd; var videoTmp = new TVideoType
{
Path = args.Path,
VideoType = VideoType.Dvd
};
Set3DFormat(videoTmp);
return videoTmp;
} }
else if (IsBluRayDirectory(filename)) else if (IsBluRayDirectory(filename))
{ {
videoType = VideoType.BluRay; var videoTmp = new TVideoType
{
Path = args.Path,
VideoType = VideoType.BluRay
};
Set3DFormat(videoTmp);
return videoTmp;
} }
} }
else if (IsDvdFile(filename)) else if (IsDvdFile(filename))

@ -323,6 +323,15 @@ public class TranscodingJobHelper : IDisposable
if (delete(job.Path!)) if (delete(job.Path!))
{ {
await DeletePartialStreamFiles(job.Path!, job.Type, 0, 1500).ConfigureAwait(false); await DeletePartialStreamFiles(job.Path!, job.Type, 0, 1500).ConfigureAwait(false);
if (job.MediaSource?.VideoType == VideoType.Dvd || job.MediaSource?.VideoType == VideoType.BluRay)
{
var concatFilePath = Path.Join(_serverConfigurationManager.GetTranscodePath(), job.MediaSource.Id + ".concat");
if (File.Exists(concatFilePath))
{
_logger.LogInformation("Deleting ffmpeg concat configuration at {Path}", concatFilePath);
File.Delete(concatFilePath);
}
}
} }
if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId)) if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId))
@ -524,7 +533,10 @@ public class TranscodingJobHelper : IDisposable
if (state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) if (state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
{ {
var attachmentPath = Path.Combine(_appPaths.CachePath, "attachments", state.MediaSource.Id); var attachmentPath = Path.Combine(_appPaths.CachePath, "attachments", state.MediaSource.Id);
await _attachmentExtractor.ExtractAllAttachments(state.MediaPath, state.MediaSource, attachmentPath, cancellationTokenSource.Token).ConfigureAwait(false); if (state.VideoType != VideoType.Dvd)
{
await _attachmentExtractor.ExtractAllAttachments(state.MediaPath, state.MediaSource, attachmentPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
if (state.SubtitleStream.IsExternal && string.Equals(Path.GetExtension(state.SubtitleStream.Path), ".mks", StringComparison.OrdinalIgnoreCase)) if (state.SubtitleStream.IsExternal && string.Equals(Path.GetExtension(state.SubtitleStream.Path), ".mks", StringComparison.OrdinalIgnoreCase))
{ {

@ -561,9 +561,12 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetInputPathArgument(EncodingJobInfo state) public string GetInputPathArgument(EncodingJobInfo state)
{ {
var mediaPath = state.MediaPath ?? string.Empty; return state.MediaSource.VideoType switch
{
return _mediaEncoder.GetInputArgument(mediaPath, state.MediaSource); VideoType.Dvd => _mediaEncoder.GetInputArgument(_mediaEncoder.GetPrimaryPlaylistVobFiles(state.MediaPath, null).ToList(), state.MediaSource),
VideoType.BluRay => _mediaEncoder.GetInputArgument(_mediaEncoder.GetPrimaryPlaylistM2tsFiles(state.MediaPath).ToList(), state.MediaSource),
_ => _mediaEncoder.GetInputArgument(state.MediaPath, state.MediaSource)
};
} }
/// <summary> /// <summary>
@ -991,8 +994,18 @@ namespace MediaBrowser.Controller.MediaEncoding
arg.Append(canvasArgs); arg.Append(canvasArgs);
} }
arg.Append(" -i ") if (state.MediaSource.VideoType == VideoType.Dvd || state.MediaSource.VideoType == VideoType.BluRay)
.Append(GetInputPathArgument(state)); {
var tmpConcatPath = Path.Join(options.TranscodingTempPath, state.MediaSource.Id + ".concat");
_mediaEncoder.GenerateConcatConfig(state.MediaSource, tmpConcatPath);
arg.Append(" -f concat -safe 0 -i ")
.Append(tmpConcatPath);
}
else
{
arg.Append(" -i ")
.Append(GetInputPathArgument(state));
}
// sub2video for external graphical subtitles // sub2video for external graphical subtitles
if (state.SubtitleStream is not null if (state.SubtitleStream is not null

@ -153,6 +153,14 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetInputArgument(string inputFile, MediaSourceInfo mediaSource); string GetInputArgument(string inputFile, MediaSourceInfo mediaSource);
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="inputFiles">The input files.</param>
/// <param name="mediaSource">The mediaSource.</param>
/// <returns>System.String.</returns>
string GetInputArgument(IReadOnlyList<string> inputFiles, MediaSourceInfo mediaSource);
/// <summary> /// <summary>
/// Gets the input argument for an external subtitle file. /// Gets the input argument for an external subtitle file.
/// </summary> /// </summary>
@ -187,5 +195,27 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <param name="pathType">The type of path.</param> /// <param name="pathType">The type of path.</param>
void UpdateEncoderPath(string path, string pathType); void UpdateEncoderPath(string path, string pathType);
/// <summary>
/// Gets the primary playlist of .vob files.
/// </summary>
/// <param name="path">The to the .vob files.</param>
/// <param name="titleNumber">The title number to start with.</param>
/// <returns>A playlist.</returns>
IReadOnlyList<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber);
/// <summary>
/// Gets the primary playlist of .m2ts files.
/// </summary>
/// <param name="path">The to the .m2ts files.</param>
/// <returns>A playlist.</returns>
IReadOnlyList<string> GetPrimaryPlaylistM2tsFiles(string path);
/// <summary>
/// Generates a FFmpeg concat config for the source.
/// </summary>
/// <param name="source">The <see cref="MediaSourceInfo"/>.</param>
/// <param name="concatFilePath">The path the config should be written to.</param>
void GenerateConcatConfig(MediaSourceInfo source, string concatFilePath);
} }
} }

@ -0,0 +1,123 @@
using System;
using System.IO;
using System.Linq;
using BDInfo.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.MediaEncoding.BdInfo;
/// <summary>
/// Class BdInfoDirectoryInfo.
/// </summary>
public class BdInfoDirectoryInfo : IDirectoryInfo
{
private readonly IFileSystem _fileSystem;
private readonly FileSystemMetadata _impl;
/// <summary>
/// Initializes a new instance of the <see cref="BdInfoDirectoryInfo" /> class.
/// </summary>
/// <param name="fileSystem">The filesystem.</param>
/// <param name="path">The path.</param>
public BdInfoDirectoryInfo(IFileSystem fileSystem, string path)
{
_fileSystem = fileSystem;
_impl = _fileSystem.GetDirectoryInfo(path);
}
private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl)
{
_fileSystem = fileSystem;
_impl = impl;
}
/// <summary>
/// Gets the name.
/// </summary>
public string Name => _impl.Name;
/// <summary>
/// Gets the full name.
/// </summary>
public string FullName => _impl.FullName;
/// <summary>
/// Gets the parent directory information.
/// </summary>
public IDirectoryInfo? Parent
{
get
{
var parentFolder = Path.GetDirectoryName(_impl.FullName);
if (parentFolder is not null)
{
return new BdInfoDirectoryInfo(_fileSystem, parentFolder);
}
return null;
}
}
/// <summary>
/// Gets the directories.
/// </summary>
/// <returns>An array with all directories.</returns>
public IDirectoryInfo[] GetDirectories()
{
return _fileSystem.GetDirectories(_impl.FullName)
.Select(x => new BdInfoDirectoryInfo(_fileSystem, x))
.ToArray();
}
/// <summary>
/// Gets the files.
/// </summary>
/// <returns>All files of the directory.</returns>
public IFileInfo[] GetFiles()
{
return _fileSystem.GetFiles(_impl.FullName)
.Select(x => new BdInfoFileInfo(x))
.ToArray();
}
/// <summary>
/// Gets the files matching a pattern.
/// </summary>
/// <param name="searchPattern">The search pattern.</param>
/// <returns>All files of the directory matchign the search pattern.</returns>
public IFileInfo[] GetFiles(string searchPattern)
{
return _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false)
.Select(x => new BdInfoFileInfo(x))
.ToArray();
}
/// <summary>
/// Gets the files matching a pattern and search options.
/// </summary>
/// <param name="searchPattern">The search pattern.</param>
/// <param name="searchOption">The search optin.</param>
/// <returns>All files of the directory matchign the search pattern and options.</returns>
public IFileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
{
return _fileSystem.GetFiles(
_impl.FullName,
new[] { searchPattern },
false,
(searchOption & SearchOption.AllDirectories) == SearchOption.AllDirectories)
.Select(x => new BdInfoFileInfo(x))
.ToArray();
}
/// <summary>
/// Gets the bdinfo of a file system path.
/// </summary>
/// <param name="fs">The file system.</param>
/// <param name="path">The path.</param>
/// <returns>The BD directory information of the path on the file system.</returns>
public static IDirectoryInfo FromFileSystemPath(IFileSystem fs, string path)
{
return new BdInfoDirectoryInfo(fs, path);
}
}

@ -0,0 +1,187 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BDInfo;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.BdInfo;
/// <summary>
/// Class BdInfoExaminer.
/// </summary>
public class BdInfoExaminer : IBlurayExaminer
{
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="BdInfoExaminer" /> class.
/// </summary>
/// <param name="fileSystem">The filesystem.</param>
public BdInfoExaminer(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
/// <summary>
/// Gets the disc info.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>BlurayDiscInfo.</returns>
public BlurayDiscInfo GetDiscInfo(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException(nameof(path));
}
var bdrom = new BDROM(BdInfoDirectoryInfo.FromFileSystemPath(_fileSystem, path));
bdrom.Scan();
// Get the longest playlist
var playlist = bdrom.PlaylistFiles.Values.OrderByDescending(p => p.TotalLength).FirstOrDefault(p => p.IsValid);
var outputStream = new BlurayDiscInfo
{
MediaStreams = Array.Empty<MediaStream>()
};
if (playlist is null)
{
return outputStream;
}
outputStream.Chapters = playlist.Chapters.ToArray();
outputStream.RunTimeTicks = TimeSpan.FromSeconds(playlist.TotalLength).Ticks;
var sortedStreams = playlist.SortedStreams;
var mediaStreams = new List<MediaStream>(sortedStreams.Count);
foreach (var stream in sortedStreams)
{
switch (stream)
{
case TSVideoStream videoStream:
AddVideoStream(mediaStreams, videoStream);
break;
case TSAudioStream audioStream:
AddAudioStream(mediaStreams, audioStream);
break;
case TSTextStream textStream:
AddSubtitleStream(mediaStreams, textStream);
break;
case TSGraphicsStream graphicStream:
AddSubtitleStream(mediaStreams, graphicStream);
break;
}
}
outputStream.MediaStreams = mediaStreams.ToArray();
outputStream.PlaylistName = playlist.Name;
if (playlist.StreamClips is not null && playlist.StreamClips.Count > 0)
{
// Get the files in the playlist
outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToArray();
}
return outputStream;
}
/// <summary>
/// Adds the video stream.
/// </summary>
/// <param name="streams">The streams.</param>
/// <param name="videoStream">The video stream.</param>
private void AddVideoStream(List<MediaStream> streams, TSVideoStream videoStream)
{
var mediaStream = new MediaStream
{
BitRate = Convert.ToInt32(videoStream.BitRate),
Width = videoStream.Width,
Height = videoStream.Height,
Codec = videoStream.CodecShortName,
IsInterlaced = videoStream.IsInterlaced,
Type = MediaStreamType.Video,
Index = streams.Count
};
if (videoStream.FrameRateDenominator > 0)
{
float frameRateEnumerator = videoStream.FrameRateEnumerator;
float frameRateDenominator = videoStream.FrameRateDenominator;
mediaStream.AverageFrameRate = mediaStream.RealFrameRate = frameRateEnumerator / frameRateDenominator;
}
streams.Add(mediaStream);
}
/// <summary>
/// Adds the audio stream.
/// </summary>
/// <param name="streams">The streams.</param>
/// <param name="audioStream">The audio stream.</param>
private void AddAudioStream(List<MediaStream> streams, TSAudioStream audioStream)
{
var stream = new MediaStream
{
Codec = audioStream.CodecShortName,
Language = audioStream.LanguageCode,
Channels = audioStream.ChannelCount,
SampleRate = audioStream.SampleRate,
Type = MediaStreamType.Audio,
Index = streams.Count
};
var bitrate = Convert.ToInt32(audioStream.BitRate);
if (bitrate > 0)
{
stream.BitRate = bitrate;
}
if (audioStream.LFE > 0)
{
stream.Channels = audioStream.ChannelCount + 1;
}
streams.Add(stream);
}
/// <summary>
/// Adds the subtitle stream.
/// </summary>
/// <param name="streams">The streams.</param>
/// <param name="textStream">The text stream.</param>
private void AddSubtitleStream(List<MediaStream> streams, TSTextStream textStream)
{
streams.Add(new MediaStream
{
Language = textStream.LanguageCode,
Codec = textStream.CodecShortName,
Type = MediaStreamType.Subtitle,
Index = streams.Count
});
}
/// <summary>
/// Adds the subtitle stream.
/// </summary>
/// <param name="streams">The streams.</param>
/// <param name="textStream">The text stream.</param>
private void AddSubtitleStream(List<MediaStream> streams, TSGraphicsStream textStream)
{
streams.Add(new MediaStream
{
Language = textStream.LanguageCode,
Codec = textStream.CodecShortName,
Type = MediaStreamType.Subtitle,
Index = streams.Count
});
}
}

@ -0,0 +1,68 @@
using System.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.MediaEncoding.BdInfo;
/// <summary>
/// Class BdInfoFileInfo.
/// </summary>
public class BdInfoFileInfo : BDInfo.IO.IFileInfo
{
private FileSystemMetadata _impl;
/// <summary>
/// Initializes a new instance of the <see cref="BdInfoFileInfo" /> class.
/// </summary>
/// <param name="impl">The <see cref="FileSystemMetadata" />.</param>
public BdInfoFileInfo(FileSystemMetadata impl)
{
_impl = impl;
}
/// <summary>
/// Gets the name.
/// </summary>
public string Name => _impl.Name;
/// <summary>
/// Gets the full name.
/// </summary>
public string FullName => _impl.FullName;
/// <summary>
/// Gets the extension.
/// </summary>
public string Extension => _impl.Extension;
/// <summary>
/// Gets the length.
/// </summary>
public long Length => _impl.Length;
/// <summary>
/// Gets a value indicating whether this is a directory.
/// </summary>
public bool IsDir => _impl.IsDirectory;
/// <summary>
/// Gets a file as file stream.
/// </summary>
/// <returns>A <see cref="FileStream" /> for the file.</returns>
public Stream OpenRead()
{
return new FileStream(
FullName,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
}
/// <summary>
/// Gets a files's content with a stream reader.
/// </summary>
/// <returns>A <see cref="StreamReader" /> for the file's content.</returns>
public StreamReader OpenText()
{
return new StreamReader(OpenRead());
}
}

@ -1,7 +1,9 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Encoder namespace MediaBrowser.MediaEncoding.Encoder
@ -15,21 +17,38 @@ namespace MediaBrowser.MediaEncoding.Encoder
return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", inputFile); return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", inputFile);
} }
return GetConcatInputArgument(inputFile, inputPrefix); return GetFileInputArgument(inputFile, inputPrefix);
}
public static string GetInputArgument(string inputPrefix, IReadOnlyList<string> inputFiles, MediaProtocol protocol)
{
if (protocol != MediaProtocol.File)
{
return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", inputFiles[0]);
}
return GetConcatInputArgument(inputFiles, inputPrefix);
} }
/// <summary> /// <summary>
/// Gets the concat input argument. /// Gets the concat input argument.
/// </summary> /// </summary>
/// <param name="inputFile">The input file.</param> /// <param name="inputFiles">The input files.</param>
/// <param name="inputPrefix">The input prefix.</param> /// <param name="inputPrefix">The input prefix.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private static string GetConcatInputArgument(string inputFile, string inputPrefix) private static string GetConcatInputArgument(IReadOnlyList<string> inputFiles, string inputPrefix)
{ {
// Get all streams // Get all streams
// If there's more than one we'll need to use the concat command // If there's more than one we'll need to use the concat command
if (inputFiles.Count > 1)
{
var files = string.Join("|", inputFiles.Select(NormalizePath));
return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
}
// Determine the input path for video files // Determine the input path for video files
return GetFileInputArgument(inputFile, inputPrefix); return GetFileInputArgument(inputFiles[0], inputPrefix);
} }
/// <summary> /// <summary>

@ -11,6 +11,7 @@ using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json;
using Jellyfin.Extensions.Json.Converters; using Jellyfin.Extensions.Json.Converters;
using MediaBrowser.Common; using MediaBrowser.Common;
@ -51,6 +52,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly IServerConfigurationManager _configurationManager; private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IBlurayExaminer _blurayExaminer;
private readonly IConfiguration _config; private readonly IConfiguration _config;
private readonly IServerConfigurationManager _serverConfig; private readonly IServerConfigurationManager _serverConfig;
private readonly string _startupOptionFFmpegPath; private readonly string _startupOptionFFmpegPath;
@ -95,6 +97,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
ILogger<MediaEncoder> logger, ILogger<MediaEncoder> logger,
IServerConfigurationManager configurationManager, IServerConfigurationManager configurationManager,
IFileSystem fileSystem, IFileSystem fileSystem,
IBlurayExaminer blurayExaminer,
ILocalizationManager localization, ILocalizationManager localization,
IConfiguration config, IConfiguration config,
IServerConfigurationManager serverConfig) IServerConfigurationManager serverConfig)
@ -102,6 +105,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger = logger; _logger = logger;
_configurationManager = configurationManager; _configurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_blurayExaminer = blurayExaminer;
_localization = localization; _localization = localization;
_config = config; _config = config;
_serverConfig = serverConfig; _serverConfig = serverConfig;
@ -117,16 +121,22 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <inheritdoc /> /// <inheritdoc />
public string ProbePath => _ffprobePath; public string ProbePath => _ffprobePath;
/// <inheritdoc />
public Version EncoderVersion => _ffmpegVersion; public Version EncoderVersion => _ffmpegVersion;
/// <inheritdoc />
public bool IsPkeyPauseSupported => _isPkeyPauseSupported; public bool IsPkeyPauseSupported => _isPkeyPauseSupported;
/// <inheritdoc />
public bool IsVaapiDeviceAmd => _isVaapiDeviceAmd; public bool IsVaapiDeviceAmd => _isVaapiDeviceAmd;
/// <inheritdoc />
public bool IsVaapiDeviceInteliHD => _isVaapiDeviceInteliHD; public bool IsVaapiDeviceInteliHD => _isVaapiDeviceInteliHD;
/// <inheritdoc />
public bool IsVaapiDeviceInteli965 => _isVaapiDeviceInteli965; public bool IsVaapiDeviceInteli965 => _isVaapiDeviceInteli965;
/// <inheritdoc />
public bool IsVaapiDeviceSupportVulkanFmtModifier => _isVaapiDeviceSupportVulkanFmtModifier; public bool IsVaapiDeviceSupportVulkanFmtModifier => _isVaapiDeviceSupportVulkanFmtModifier;
/// <summary> /// <summary>
@ -344,26 +354,31 @@ namespace MediaBrowser.MediaEncoding.Encoder
_ffmpegVersion = validator.GetFFmpegVersion(); _ffmpegVersion = validator.GetFFmpegVersion();
} }
/// <inheritdoc />
public bool SupportsEncoder(string encoder) public bool SupportsEncoder(string encoder)
{ {
return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase); return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
} }
/// <inheritdoc />
public bool SupportsDecoder(string decoder) public bool SupportsDecoder(string decoder)
{ {
return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase); return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
} }
/// <inheritdoc />
public bool SupportsHwaccel(string hwaccel) public bool SupportsHwaccel(string hwaccel)
{ {
return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase); return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
} }
/// <inheritdoc />
public bool SupportsFilter(string filter) public bool SupportsFilter(string filter)
{ {
return _filters.Contains(filter, StringComparer.OrdinalIgnoreCase); return _filters.Contains(filter, StringComparer.OrdinalIgnoreCase);
} }
/// <inheritdoc />
public bool SupportsFilterWithOption(FilterOptionType option) public bool SupportsFilterWithOption(FilterOptionType option)
{ {
if (_filtersWithOption.TryGetValue((int)option, out var val)) if (_filtersWithOption.TryGetValue((int)option, out var val))
@ -394,24 +409,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
return true; return true;
} }
/// <summary> /// <inheritdoc />
/// Gets the media info.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken) public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
{ {
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters; var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
var inputFile = request.MediaSource.Path;
string analyzeDuration = string.Empty; string analyzeDuration = string.Empty;
string ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty; string ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
if (request.MediaSource.AnalyzeDurationMs > 0) if (request.MediaSource.AnalyzeDurationMs > 0)
{ {
analyzeDuration = "-analyzeduration " + analyzeDuration = "-analyzeduration " + (request.MediaSource.AnalyzeDurationMs * 1000).ToString();
(request.MediaSource.AnalyzeDurationMs * 1000).ToString();
} }
else if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration)) else if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration))
{ {
@ -419,7 +426,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
return GetMediaInfoInternal( return GetMediaInfoInternal(
GetInputArgument(inputFile, request.MediaSource), GetInputArgument(request.MediaSource.Path, request.MediaSource),
request.MediaSource.Path, request.MediaSource.Path,
request.MediaSource.Protocol, request.MediaSource.Protocol,
extractChapters, extractChapters,
@ -429,36 +436,30 @@ namespace MediaBrowser.MediaEncoding.Encoder
cancellationToken); cancellationToken);
} }
/// <summary> /// <inheritdoc />
/// Gets the input argument. public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaSourceInfo mediaSource)
/// </summary> {
/// <param name="inputFile">The input file.</param> return EncodingUtils.GetInputArgument("file", inputFiles, mediaSource.Protocol);
/// <param name="mediaSource">The mediaSource.</param> }
/// <returns>System.String.</returns>
/// <exception cref="ArgumentException">Unrecognized InputType.</exception> /// <inheritdoc />
public string GetInputArgument(string inputFile, MediaSourceInfo mediaSource) public string GetInputArgument(string inputFile, MediaSourceInfo mediaSource)
{ {
var prefix = "file"; var prefix = "file";
if (mediaSource.VideoType == VideoType.BluRay if (mediaSource.IsoType == IsoType.BluRay)
|| mediaSource.IsoType == IsoType.BluRay)
{ {
prefix = "bluray"; prefix = "bluray";
} }
return EncodingUtils.GetInputArgument(prefix, inputFile, mediaSource.Protocol); return EncodingUtils.GetInputArgument(prefix, new[] { inputFile }, mediaSource.Protocol);
} }
/// <summary> /// <inheritdoc />
/// Gets the input argument for an external subtitle file.
/// </summary>
/// <param name="inputFile">The input file.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentException">Unrecognized InputType.</exception>
public string GetExternalSubtitleInputArgument(string inputFile) public string GetExternalSubtitleInputArgument(string inputFile)
{ {
const string Prefix = "file"; const string Prefix = "file";
return EncodingUtils.GetInputArgument(Prefix, inputFile, MediaProtocol.File); return EncodingUtils.GetInputArgument(Prefix, new[] { inputFile }, MediaProtocol.File);
} }
/// <summary> /// <summary>
@ -549,6 +550,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
} }
/// <inheritdoc />
public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken) public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
{ {
var mediaSource = new MediaSourceInfo var mediaSource = new MediaSourceInfo
@ -559,11 +561,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
return ExtractImage(path, null, null, imageStreamIndex, mediaSource, true, null, null, ImageFormat.Jpg, cancellationToken); return ExtractImage(path, null, null, imageStreamIndex, mediaSource, true, null, null, ImageFormat.Jpg, cancellationToken);
} }
/// <inheritdoc />
public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
{ {
return ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, ImageFormat.Jpg, cancellationToken); return ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, ImageFormat.Jpg, cancellationToken);
} }
/// <inheritdoc />
public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, ImageFormat? targetFormat, CancellationToken cancellationToken) public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, ImageFormat? targetFormat, CancellationToken cancellationToken)
{ {
return ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, targetFormat, cancellationToken); return ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, targetFormat, cancellationToken);
@ -767,6 +771,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
} }
/// <inheritdoc />
public string GetTimeParameter(long ticks) public string GetTimeParameter(long ticks)
{ {
var time = TimeSpan.FromTicks(ticks); var time = TimeSpan.FromTicks(ticks);
@ -865,6 +870,114 @@ namespace MediaBrowser.MediaEncoding.Encoder
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <inheritdoc />
public IReadOnlyList<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber)
{
// Eliminate menus and intros by omitting VIDEO_TS.VOB and all subsequent title .vob files ending with _0.VOB
var allVobs = _fileSystem.GetFiles(path, true)
.Where(file => string.Equals(file.Extension, ".VOB", StringComparison.OrdinalIgnoreCase))
.Where(file => !string.Equals(file.Name, "VIDEO_TS.VOB", StringComparison.OrdinalIgnoreCase))
.Where(file => !file.Name.EndsWith("_0.VOB", StringComparison.OrdinalIgnoreCase))
.OrderBy(i => i.FullName)
.ToList();
if (titleNumber.HasValue)
{
var prefix = string.Format(CultureInfo.InvariantCulture, "VTS_{0:D2}_", titleNumber.Value);
var vobs = allVobs.Where(i => i.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList();
if (vobs.Count > 0)
{
return vobs.Select(i => i.FullName).ToList();
}
_logger.LogWarning("Could not determine .vob files for title {Title} of {Path}.", titleNumber, path);
}
// Check for multiple big titles (> 900 MB)
var titles = allVobs
.Where(vob => vob.Length >= 900 * 1024 * 1024)
.Select(vob => _fileSystem.GetFileNameWithoutExtension(vob).AsSpan().RightPart('_').ToString())
.Distinct()
.ToList();
// Fall back to first title if no big title is found
if (titles.Count == 0)
{
titles.Add(_fileSystem.GetFileNameWithoutExtension(allVobs[0]).AsSpan().RightPart('_').ToString());
}
// Aggregate all .vob files of the titles
return allVobs
.Where(vob => titles.Contains(_fileSystem.GetFileNameWithoutExtension(vob).AsSpan().RightPart('_').ToString()))
.Select(i => i.FullName)
.ToList();
}
/// <inheritdoc />
public IReadOnlyList<string> GetPrimaryPlaylistM2tsFiles(string path)
{
// Get all playable .m2ts files
var validPlaybackFiles = _blurayExaminer.GetDiscInfo(path).Files;
// Get all files from the BDMV/STREAMING directory
var directoryFiles = _fileSystem.GetFiles(Path.Join(path, "BDMV", "STREAM"));
// Only return playable local .m2ts files
return directoryFiles
.Where(f => validPlaybackFiles.Contains(f.Name, StringComparer.OrdinalIgnoreCase))
.Select(f => f.FullName)
.ToList();
}
/// <inheritdoc />
public void GenerateConcatConfig(MediaSourceInfo source, string concatFilePath)
{
// Get all playable files
IReadOnlyList<string> files;
var videoType = source.VideoType;
if (videoType == VideoType.Dvd)
{
files = GetPrimaryPlaylistVobFiles(source.Path, null);
}
else if (videoType == VideoType.BluRay)
{
files = GetPrimaryPlaylistM2tsFiles(source.Path);
}
else
{
return;
}
// Generate concat configuration entries for each file and write to file
using (StreamWriter sw = new StreamWriter(concatFilePath))
{
foreach (var path in files)
{
var mediaInfoResult = GetMediaInfo(
new MediaInfoRequest
{
MediaType = DlnaProfileType.Video,
MediaSource = new MediaSourceInfo
{
Path = path,
Protocol = MediaProtocol.File,
VideoType = videoType
}
},
CancellationToken.None).GetAwaiter().GetResult();
var duration = TimeSpan.FromTicks(mediaInfoResult.RunTimeTicks.Value).TotalSeconds;
// Add file path stanza to concat configuration
sw.WriteLine("file '{0}'", path);
// Add duration stanza to concat configuration
sw.WriteLine("duration {0}", duration);
}
}
}
public bool CanExtractSubtitles(string codec) public bool CanExtractSubtitles(string codec)
{ {
// TODO is there ever a case when a subtitle can't be extracted?? // TODO is there ever a case when a subtitle can't be extracted??

@ -22,6 +22,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BDInfo" />
<PackageReference Include="libse" /> <PackageReference Include="libse" />
<PackageReference Include="Microsoft.Extensions.Http" /> <PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="System.Text.Encoding.CodePages" /> <PackageReference Include="System.Text.Encoding.CodePages" />

@ -248,12 +248,23 @@ namespace MediaBrowser.MediaEncoding.Probing
return null; return null;
} }
// Handle MPEG-1 container
if (string.Equals(format, "mpegvideo", StringComparison.OrdinalIgnoreCase)) if (string.Equals(format, "mpegvideo", StringComparison.OrdinalIgnoreCase))
{ {
return "mpeg"; return "mpeg";
} }
format = format.Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); // Handle MPEG-2 container
if (string.Equals(format, "mpeg", StringComparison.OrdinalIgnoreCase))
{
return "ts";
}
// Handle matroska container
if (string.Equals(format, "matroska", StringComparison.OrdinalIgnoreCase))
{
return "mkv";
}
return format; return format;
} }

@ -623,6 +623,12 @@ namespace MediaBrowser.Model.Dlna
var isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || !bitrateLimitExceeded); var isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || !bitrateLimitExceeded);
TranscodeReason transcodeReasons = 0; TranscodeReason transcodeReasons = 0;
// Force transcode or remux for BD/DVD folders
if (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay)
{
isEligibleForDirectPlay = false;
}
if (bitrateLimitExceeded) if (bitrateLimitExceeded)
{ {
transcodeReasons = TranscodeReason.ContainerBitrateExceedsLimit; transcodeReasons = TranscodeReason.ContainerBitrateExceedsLimit;

@ -107,9 +107,8 @@ namespace MediaBrowser.Model.Dlna
public string MediaSourceId => MediaSource?.Id; public string MediaSourceId => MediaSource?.Id;
public bool IsDirectStream => public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay)
PlayMethod == PlayMethod.DirectStream || && PlayMethod is PlayMethod.DirectStream or PlayMethod.DirectPlay;
PlayMethod == PlayMethod.DirectPlay;
/// <summary> /// <summary>
/// Gets the audio stream that will be used. /// Gets the audio stream that will be used.

@ -0,0 +1,41 @@
#nullable disable
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.MediaInfo;
/// <summary>
/// Represents the result of BDInfo output.
/// </summary>
public class BlurayDiscInfo
{
/// <summary>
/// Gets or sets the media streams.
/// </summary>
/// <value>The media streams.</value>
public MediaStream[] MediaStreams { get; set; }
/// <summary>
/// Gets or sets the run time ticks.
/// </summary>
/// <value>The run time ticks.</value>
public long? RunTimeTicks { get; set; }
/// <summary>
/// Gets or sets the files.
/// </summary>
/// <value>The files.</value>
public string[] Files { get; set; }
/// <summary>
/// Gets or sets the playlist name.
/// </summary>
/// <value>The playlist name.</value>
public string PlaylistName { get; set; }
/// <summary>
/// Gets or sets the chapters.
/// </summary>
/// <value>The chapters.</value>
public double[] Chapters { get; set; }
}

@ -0,0 +1,14 @@
namespace MediaBrowser.Model.MediaInfo;
/// <summary>
/// Interface IBlurayExaminer.
/// </summary>
public interface IBlurayExaminer
{
/// <summary>
/// Gets the disc info.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>BlurayDiscInfo.</returns>
BlurayDiscInfo GetDiscInfo(string path);
}

@ -36,6 +36,7 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ILogger<FFProbeVideoInfo> _logger; private readonly ILogger<FFProbeVideoInfo> _logger;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
private readonly IBlurayExaminer _blurayExaminer;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IEncodingManager _encodingManager; private readonly IEncodingManager _encodingManager;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
@ -51,6 +52,7 @@ namespace MediaBrowser.Providers.MediaInfo
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
IItemRepository itemRepo, IItemRepository itemRepo,
IBlurayExaminer blurayExaminer,
ILocalizationManager localization, ILocalizationManager localization,
IEncodingManager encodingManager, IEncodingManager encodingManager,
IServerConfigurationManager config, IServerConfigurationManager config,
@ -64,6 +66,7 @@ namespace MediaBrowser.Providers.MediaInfo
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_itemRepo = itemRepo; _itemRepo = itemRepo;
_blurayExaminer = blurayExaminer;
_localization = localization; _localization = localization;
_encodingManager = encodingManager; _encodingManager = encodingManager;
_config = config; _config = config;
@ -80,16 +83,77 @@ namespace MediaBrowser.Providers.MediaInfo
CancellationToken cancellationToken) CancellationToken cancellationToken)
where T : Video where T : Video
{ {
BlurayDiscInfo blurayDiscInfo = null;
Model.MediaInfo.MediaInfo mediaInfoResult = null; Model.MediaInfo.MediaInfo mediaInfoResult = null;
if (!item.IsShortcut || options.EnableRemoteContentProbe) if (!item.IsShortcut || options.EnableRemoteContentProbe)
{ {
mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false); if (item.VideoType == VideoType.Dvd)
{
// Get list of playable .vob files
var vobs = _mediaEncoder.GetPrimaryPlaylistVobFiles(item.Path, null);
// Return if no playable .vob files are found
if (vobs.Count == 0)
{
_logger.LogError("No playable .vob files found in DVD structure, skipping FFprobe.");
return ItemUpdateType.MetadataImport;
}
// Fetch metadata of first .vob file
mediaInfoResult = await GetMediaInfo(
new Video
{
Path = vobs[0]
},
cancellationToken).ConfigureAwait(false);
// Sum up the runtime of all .vob files skipping the first .vob
for (var i = 1; i < vobs.Count; i++)
{
var tmpMediaInfo = await GetMediaInfo(
new Video
{
Path = vobs[i]
},
cancellationToken).ConfigureAwait(false);
mediaInfoResult.RunTimeTicks += tmpMediaInfo.RunTimeTicks;
}
}
else if (item.VideoType == VideoType.BluRay)
{
// Get BD disc information
blurayDiscInfo = GetBDInfo(item.Path);
// Get playable .m2ts files
var m2ts = _mediaEncoder.GetPrimaryPlaylistM2tsFiles(item.Path);
// Return if no playable .m2ts files are found
if (blurayDiscInfo.Files.Length == 0 || m2ts.Count == 0)
{
_logger.LogError("No playable .m2ts files found in Blu-ray structure, skipping FFprobe.");
return ItemUpdateType.MetadataImport;
}
// Fetch metadata of first .m2ts file
mediaInfoResult = await GetMediaInfo(
new Video
{
Path = m2ts[0]
},
cancellationToken).ConfigureAwait(false);
}
else
{
mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
} }
await Fetch(item, cancellationToken, mediaInfoResult, options).ConfigureAwait(false); await Fetch(item, cancellationToken, mediaInfoResult, blurayDiscInfo, options).ConfigureAwait(false);
return ItemUpdateType.MetadataImport; return ItemUpdateType.MetadataImport;
} }
@ -129,6 +193,7 @@ namespace MediaBrowser.Providers.MediaInfo
Video video, Video video,
CancellationToken cancellationToken, CancellationToken cancellationToken,
Model.MediaInfo.MediaInfo mediaInfo, Model.MediaInfo.MediaInfo mediaInfo,
BlurayDiscInfo blurayInfo,
MetadataRefreshOptions options) MetadataRefreshOptions options)
{ {
List<MediaStream> mediaStreams; List<MediaStream> mediaStreams;
@ -153,19 +218,8 @@ namespace MediaBrowser.Providers.MediaInfo
} }
mediaAttachments = mediaInfo.MediaAttachments; mediaAttachments = mediaInfo.MediaAttachments;
video.TotalBitrate = mediaInfo.Bitrate; video.TotalBitrate = mediaInfo.Bitrate;
// video.FormatName = (mediaInfo.Container ?? string.Empty) video.RunTimeTicks = mediaInfo.RunTimeTicks;
// .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
// For DVDs this may not always be accurate, so don't set the runtime if the item already has one
var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks is null || video.RunTimeTicks.Value == 0;
if (needToSetRuntime)
{
video.RunTimeTicks = mediaInfo.RunTimeTicks;
}
video.Size = mediaInfo.Size; video.Size = mediaInfo.Size;
if (video.VideoType == VideoType.VideoFile) if (video.VideoType == VideoType.VideoFile)
@ -182,6 +236,10 @@ namespace MediaBrowser.Providers.MediaInfo
video.Container = mediaInfo.Container; video.Container = mediaInfo.Container;
chapters = mediaInfo.Chapters ?? Array.Empty<ChapterInfo>(); chapters = mediaInfo.Chapters ?? Array.Empty<ChapterInfo>();
if (blurayInfo is not null)
{
FetchBdInfo(video, ref chapters, mediaStreams, blurayInfo);
}
} }
else else
{ {
@ -277,6 +335,86 @@ namespace MediaBrowser.Providers.MediaInfo
} }
} }
private void FetchBdInfo(Video video, ref ChapterInfo[] chapters, List<MediaStream> mediaStreams, BlurayDiscInfo blurayInfo)
{
if (blurayInfo.Files.Length <= 1)
{
return;
}
// Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output
int? currentHeight = null;
int? currentWidth = null;
int? currentBitRate = null;
var videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
// Grab the values that ffprobe recorded
if (videoStream is not null)
{
currentBitRate = videoStream.BitRate;
currentWidth = videoStream.Width;
currentHeight = videoStream.Height;
}
// Fill video properties from the BDInfo result
mediaStreams.Clear();
mediaStreams.AddRange(blurayInfo.MediaStreams);
if (blurayInfo.RunTimeTicks.HasValue && blurayInfo.RunTimeTicks.Value > 0)
{
video.RunTimeTicks = blurayInfo.RunTimeTicks;
}
if (blurayInfo.Chapters is not null)
{
double[] brChapter = blurayInfo.Chapters;
chapters = new ChapterInfo[brChapter.Length];
for (int i = 0; i < brChapter.Length; i++)
{
chapters[i] = new ChapterInfo
{
StartPositionTicks = TimeSpan.FromSeconds(brChapter[i]).Ticks
};
}
}
videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
// Use the ffprobe values if these are empty
if (videoStream is not null)
{
videoStream.BitRate = IsEmpty(videoStream.BitRate) ? currentBitRate : videoStream.BitRate;
videoStream.Width = IsEmpty(videoStream.Width) ? currentWidth : videoStream.Width;
videoStream.Height = IsEmpty(videoStream.Height) ? currentHeight : videoStream.Height;
}
}
private bool IsEmpty(int? num)
{
return !num.HasValue || num.Value == 0;
}
/// <summary>
/// Gets information about the longest playlist on a bdrom.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>VideoStream.</returns>
private BlurayDiscInfo GetBDInfo(string path)
{
ArgumentException.ThrowIfNullOrEmpty(path);
try
{
return _blurayExaminer.GetDiscInfo(path);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting BDInfo");
return null;
}
}
private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions refreshOptions, LibraryOptions libraryOptions) private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions refreshOptions, LibraryOptions libraryOptions)
{ {
var replaceData = refreshOptions.ReplaceAllMetadata; var replaceData = refreshOptions.ReplaceAllMetadata;

@ -53,6 +53,7 @@ namespace MediaBrowser.Providers.MediaInfo
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> 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="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
/// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> 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="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
@ -66,6 +67,7 @@ namespace MediaBrowser.Providers.MediaInfo
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
IItemRepository itemRepo, IItemRepository itemRepo,
IBlurayExaminer blurayExaminer,
ILocalizationManager localization, ILocalizationManager localization,
IEncodingManager encodingManager, IEncodingManager encodingManager,
IServerConfigurationManager config, IServerConfigurationManager config,
@ -85,6 +87,7 @@ namespace MediaBrowser.Providers.MediaInfo
mediaSourceManager, mediaSourceManager,
mediaEncoder, mediaEncoder,
itemRepo, itemRepo,
blurayExaminer,
localization, localization,
encodingManager, encodingManager,
config, config,

Loading…
Cancel
Save