Merge branch 'dev' into project-updates

This commit is contained in:
Anthony Lavado
2019-01-02 14:02:48 -05:00
committed by GitHub
18 changed files with 85 additions and 1025 deletions

View File

@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using MediaBrowser.Model.Diagnostics;
using Microsoft.Extensions.Logging;
@@ -18,21 +19,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
_processFactory = processFactory;
}
public Tuple<List<string>, List<string>> Validate(string encoderPath)
public (IEnumerable<string> decoders, IEnumerable<string> encoders) Validate(string encoderPath)
{
_logger.LogInformation("Validating media encoder at {0}", encoderPath);
_logger.LogInformation("Validating media encoder at {EncoderPath}", encoderPath);
var decoders = GetDecoders(encoderPath);
var encoders = GetEncoders(encoderPath);
var decoders = GetCodecs(encoderPath, Codec.Decoder);
var encoders = GetCodecs(encoderPath, Codec.Encoder);
_logger.LogInformation("Encoder validation complete");
return new Tuple<List<string>, List<string>>(decoders, encoders);
return (decoders, encoders);
}
public bool ValidateVersion(string encoderAppPath, bool logOutput)
{
string output = string.Empty;
string output = null;
try
{
output = GetProcessOutput(encoderAppPath, "-version");
@@ -71,20 +72,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return true;
}
private List<string> GetDecoders(string encoderAppPath)
{
string output = string.Empty;
try
{
output = GetProcessOutput(encoderAppPath, "-decoders");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error detecting available decoders");
}
var found = new List<string>();
var required = new[]
private static readonly string[] requiredDecoders = new[]
{
"mpeg2video",
"h264_qsv",
@@ -101,33 +89,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
"hevc"
};
foreach (var codec in required)
{
var srch = " " + codec + " ";
if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) != -1)
{
_logger.LogInformation("Decoder available: " + codec);
found.Add(codec);
}
}
return found;
}
private List<string> GetEncoders(string encoderAppPath)
{
string output = null;
try
{
output = GetProcessOutput(encoderAppPath, "-encoders");
}
catch
{
}
var found = new List<string>();
var required = new[]
private static readonly string[] requiredEncoders = new[]
{
"libx264",
"libx265",
@@ -151,32 +113,46 @@ namespace MediaBrowser.MediaEncoding.Encoder
"ac3"
};
output = output ?? string.Empty;
private enum Codec
{
Encoder,
Decoder
}
var index = 0;
foreach (var codec in required)
private IEnumerable<string> GetCodecs(string encoderAppPath, Codec codec)
{
string codecstr = codec == Codec.Encoder ? "encoders" : "decoders";
string output = null;
try
{
var srch = " " + codec + " ";
if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) != -1)
{
if (index < required.Length - 1)
{
_logger.LogInformation("Encoder available: " + codec);
}
found.Add(codec);
}
index++;
output = GetProcessOutput(encoderAppPath, "-" + codecstr);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error detecting available {Codec}", codecstr);
}
if (string.IsNullOrWhiteSpace(output))
{
return Enumerable.Empty<string>();
}
var required = codec == Codec.Encoder ? requiredEncoders : requiredDecoders;
var found = Regex
.Matches(output, @"^\s\S{6}\s(?<codec>[\w|-]+)\s+.+$", RegexOptions.Multiline)
.Cast<Match>()
.Select(x => x.Groups["codec"].Value)
.Where(x => required.Contains(x));
_logger.LogInformation("Available {Codec}: {Codecs}", codecstr, found);
return found;
}
private string GetProcessOutput(string path, string arguments)
{
var process = _processFactory.Create(new ProcessOptions
IProcess process = _processFactory.Create(new ProcessOptions
{
CreateNoWindow = true,
UseShellExecute = false,
@@ -187,7 +163,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
RedirectStandardOutput = true
});
_logger.LogInformation("Running {path} {arguments}", path, arguments);
_logger.LogInformation("Running {Path} {Arguments}", path, arguments);
using (process)
{

View File

@@ -1,179 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class FontConfigLoader
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
private readonly IZipClient _zipClient;
private readonly IFileSystem _fileSystem;
private readonly string[] _fontUrls =
{
"https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z"
};
public FontConfigLoader(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, IZipClient zipClient, IFileSystem fileSystem)
{
_httpClient = httpClient;
_appPaths = appPaths;
_logger = logger;
_zipClient = zipClient;
_fileSystem = fileSystem;
}
/// <summary>
/// Extracts the fonts.
/// </summary>
/// <param name="targetPath">The target path.</param>
/// <returns>Task.</returns>
public async Task DownloadFonts(string targetPath)
{
try
{
var fontsDirectory = Path.Combine(targetPath, "fonts");
_fileSystem.CreateDirectory(fontsDirectory);
const string fontFilename = "ARIALUNI.TTF";
var fontFile = Path.Combine(fontsDirectory, fontFilename);
if (_fileSystem.FileExists(fontFile))
{
await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
}
else
{
// Kick this off, but no need to wait on it
var task = Task.Run(async () =>
{
await DownloadFontFile(fontsDirectory, fontFilename, new SimpleProgress<double>()).ConfigureAwait(false);
await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
});
}
}
catch (HttpException ex)
{
// Don't let the server crash because of this
_logger.LogError(ex, "Error downloading ffmpeg font files");
}
catch (Exception ex)
{
// Don't let the server crash because of this
_logger.LogError(ex, "Error writing ffmpeg font files");
}
}
/// <summary>
/// Downloads the font file.
/// </summary>
/// <param name="fontsDirectory">The fonts directory.</param>
/// <param name="fontFilename">The font filename.</param>
/// <returns>Task.</returns>
private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress)
{
var existingFile = _fileSystem
.GetFilePaths(_appPaths.ProgramDataPath, true)
.FirstOrDefault(i => string.Equals(fontFilename, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase));
if (existingFile != null)
{
try
{
_fileSystem.CopyFile(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
return;
}
catch (IOException ex)
{
// Log this, but don't let it fail the operation
_logger.LogError(ex, "Error copying file");
}
}
string tempFile = null;
foreach (var url in _fontUrls)
{
progress.Report(0);
try
{
tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
{
Url = url,
Progress = progress
}).ConfigureAwait(false);
break;
}
catch (Exception ex)
{
// The core can function without the font file, so handle this
_logger.LogError(ex, "Failed to download ffmpeg font file from {url}", url);
}
}
if (string.IsNullOrEmpty(tempFile))
{
return;
}
Extract7zArchive(tempFile, fontsDirectory);
try
{
_fileSystem.DeleteFile(tempFile);
}
catch (IOException ex)
{
// Log this, but don't let it fail the operation
_logger.LogError(ex, "Error deleting temp file {path}", tempFile);
}
}
private void Extract7zArchive(string archivePath, string targetPath)
{
_logger.LogInformation("Extracting {ArchivePath} to {TargetPath}", archivePath, targetPath);
_zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
}
/// <summary>
/// Writes the font config file.
/// </summary>
/// <param name="fontsDirectory">The fonts directory.</param>
/// <returns>Task.</returns>
private async Task WriteFontConfigFile(string fontsDirectory)
{
const string fontConfigFilename = "fonts.conf";
var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
if (!_fileSystem.FileExists(fontConfigFile))
{
var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
var bytes = Encoding.UTF8.GetBytes(contents);
using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileOpenMode.Create, FileAccessMode.Write,
FileShareMode.Read, true))
{
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
}
}
}
}

View File

@@ -70,13 +70,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly string _originalFFMpegPath;
private readonly string _originalFFProbePath;
private readonly int DefaultImageExtractionTimeoutMs;
private readonly bool EnableEncoderFontFile;
private readonly IEnvironmentInfo _environmentInfo;
public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IProcessFactory processFactory,
public MediaEncoder(ILogger logger,
IJsonSerializer jsonSerializer,
string ffMpegPath,
string ffProbePath,
bool hasExternalEncoder,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
ILiveTvManager liveTvManager,
IIsoManager isoManager,
ILibraryManager libraryManager,
IChannelManager channelManager,
ISessionManager sessionManager,
Func<ISubtitleEncoder> subtitleEncoder,
Func<IMediaSourceManager> mediaSourceManager,
IHttpClient httpClient,
IZipClient zipClient,
IProcessFactory processFactory,
int defaultImageExtractionTimeoutMs,
bool enableEncoderFontFile, IEnvironmentInfo environmentInfo)
IEnvironmentInfo environmentInfo)
{
_logger = logger;
_jsonSerializer = jsonSerializer;
@@ -93,7 +107,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
_zipClient = zipClient;
_processFactory = processFactory;
DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
EnableEncoderFontFile = enableEncoderFontFile;
_environmentInfo = environmentInfo;
FFProbePath = ffProbePath;
FFMpegPath = ffMpegPath;
@@ -175,18 +188,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
SetAvailableDecoders(result.Item1);
SetAvailableEncoders(result.Item2);
if (EnableEncoderFontFile)
{
var directory = FileSystem.GetDirectoryName(FFMpegPath);
if (!string.IsNullOrWhiteSpace(directory) && FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.ProgramDataPath, directory))
{
new FontConfigLoader(_httpClient, ConfigurationManager.ApplicationPaths, _logger, _zipClient, FileSystem).DownloadFonts(directory).ConfigureAwait(false);
}
}
SetAvailableDecoders(result.decoders);
SetAvailableEncoders(result.encoders);
}
}
@@ -401,14 +404,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
private List<string> _encoders = new List<string>();
public void SetAvailableEncoders(List<string> list)
public void SetAvailableEncoders(IEnumerable<string> list)
{
_encoders = list.ToList();
//_logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
}
private List<string> _decoders = new List<string>();
public void SetAvailableDecoders(List<string> list)
public void SetAvailableDecoders(IEnumerable<string> list)
{
_decoders = list.ToList();
//_logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));