diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 4b397b328..81857e57c 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -23,6 +23,7 @@
- [fruhnow](https://github.com/fruhnow)
- [Lynxy](https://github.com/Lynxy)
- [fasheng](https://github.com/fasheng)
+ - [ploughpuff](https://github.com/ploughpuff)
# Emby Contributors
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index b3bb4f740..dc971ea59 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -28,7 +28,6 @@ using Emby.Server.Implementations.Data;
using Emby.Server.Implementations.Devices;
using Emby.Server.Implementations.Diagnostics;
using Emby.Server.Implementations.Dto;
-using Emby.Server.Implementations.FFMpeg;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.Security;
using Emby.Server.Implementations.IO;
@@ -535,7 +534,7 @@ namespace Emby.Server.Implementations
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
- MediaEncoder.Init();
+ MediaEncoder.SetFFmpegPath();
//if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath))
//{
@@ -790,7 +789,18 @@ namespace Emby.Server.Implementations
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
serviceCollection.AddSingleton(ChapterManager);
- RegisterMediaEncoder(serviceCollection);
+ MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
+ LoggerFactory,
+ JsonSerializer,
+ StartupOptions.FFmpegPath,
+ StartupOptions.FFprobePath,
+ ServerConfigurationManager,
+ FileSystemManager,
+ () => SubtitleEncoder,
+ () => MediaSourceManager,
+ ProcessFactory,
+ 5000);
+ serviceCollection.AddSingleton(MediaEncoder);
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
serviceCollection.AddSingleton(EncodingManager);
@@ -906,85 +916,6 @@ namespace Emby.Server.Implementations
return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
}
- protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
- {
- var info = new FFMpegInstallInfo();
-
- // Windows builds: http://ffmpeg.zeranoe.com/builds/
- // Linux builds: http://johnvansickle.com/ffmpeg/
- // OS X builds: http://ffmpegmac.net/
- // OS X x64: http://www.evermeet.cx/ffmpeg/
-
- if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux)
- {
- info.FFMpegFilename = "ffmpeg";
- info.FFProbeFilename = "ffprobe";
- info.ArchiveType = "7z";
- info.Version = "20170308";
- }
- else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
- {
- info.FFMpegFilename = "ffmpeg.exe";
- info.FFProbeFilename = "ffprobe.exe";
- info.Version = "20170308";
- info.ArchiveType = "7z";
- }
- else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
- {
- info.FFMpegFilename = "ffmpeg";
- info.FFProbeFilename = "ffprobe";
- info.ArchiveType = "7z";
- info.Version = "20170308";
- }
-
- return info;
- }
-
- protected virtual FFMpegInfo GetFFMpegInfo()
- {
- return new FFMpegLoader(ApplicationPaths, FileSystemManager, GetFfmpegInstallInfo())
- .GetFFMpegInfo(StartupOptions);
- }
-
- ///
- /// Registers the media encoder.
- ///
- /// Task.
- private void RegisterMediaEncoder(IServiceCollection serviceCollection)
- {
- string encoderPath = null;
- string probePath = null;
-
- var info = GetFFMpegInfo();
-
- encoderPath = info.EncoderPath;
- probePath = info.ProbePath;
- var hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase);
-
- var mediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
- LoggerFactory,
- JsonSerializer,
- encoderPath,
- probePath,
- hasExternalEncoder,
- ServerConfigurationManager,
- FileSystemManager,
- LiveTvManager,
- IsoManager,
- LibraryManager,
- ChannelManager,
- SessionManager,
- () => SubtitleEncoder,
- () => MediaSourceManager,
- HttpClient,
- ZipClient,
- ProcessFactory,
- 5000);
-
- MediaEncoder = mediaEncoder;
- serviceCollection.AddSingleton(MediaEncoder);
- }
-
///
/// Gets the user repository.
///
@@ -1460,7 +1391,7 @@ namespace Emby.Server.Implementations
ServerName = FriendlyName,
LocalAddress = localAddress,
SupportsLibraryMonitor = true,
- EncoderLocationType = MediaEncoder.EncoderLocationType,
+ EncoderLocation = MediaEncoder.EncoderLocation,
SystemArchitecture = EnvironmentInfo.SystemArchitecture,
SystemUpdateLevel = SystemUpdateLevel,
PackageName = StartupOptions.PackageName
diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs b/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs
deleted file mode 100644
index 60cd7b3d7..000000000
--- a/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace Emby.Server.Implementations.FFMpeg
-{
- ///
- /// Class FFMpegInfo
- ///
- public class FFMpegInfo
- {
- ///
- /// Gets or sets the path.
- ///
- /// The path.
- public string EncoderPath { get; set; }
- ///
- /// Gets or sets the probe path.
- ///
- /// The probe path.
- public string ProbePath { get; set; }
- ///
- /// Gets or sets the version.
- ///
- /// The version.
- public string Version { get; set; }
- }
-}
diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs b/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs
deleted file mode 100644
index fa9cb5e01..000000000
--- a/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Emby.Server.Implementations.FFMpeg
-{
- public class FFMpegInstallInfo
- {
- public string Version { get; set; }
- public string FFMpegFilename { get; set; }
- public string FFProbeFilename { get; set; }
- public string ArchiveType { get; set; }
-
- public FFMpegInstallInfo()
- {
- Version = "Path";
- FFMpegFilename = "ffmpeg";
- FFProbeFilename = "ffprobe";
- }
- }
-}
diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
deleted file mode 100644
index bbf51dd24..000000000
--- a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.IO;
-
-namespace Emby.Server.Implementations.FFMpeg
-{
- public class FFMpegLoader
- {
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
- private readonly FFMpegInstallInfo _ffmpegInstallInfo;
-
- public FFMpegLoader(IApplicationPaths appPaths, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
- {
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- _ffmpegInstallInfo = ffmpegInstallInfo;
- }
-
- public FFMpegInfo GetFFMpegInfo(IStartupOptions options)
- {
- var customffMpegPath = options.FFmpegPath;
- var customffProbePath = options.FFprobePath;
-
- if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath))
- {
- return new FFMpegInfo
- {
- ProbePath = customffProbePath,
- EncoderPath = customffMpegPath,
- Version = "external"
- };
- }
-
- var downloadInfo = _ffmpegInstallInfo;
-
- var prebuiltFolder = _appPaths.ProgramSystemPath;
- var prebuiltffmpeg = Path.Combine(prebuiltFolder, downloadInfo.FFMpegFilename);
- var prebuiltffprobe = Path.Combine(prebuiltFolder, downloadInfo.FFProbeFilename);
- if (File.Exists(prebuiltffmpeg) && File.Exists(prebuiltffprobe))
- {
- return new FFMpegInfo
- {
- ProbePath = prebuiltffprobe,
- EncoderPath = prebuiltffmpeg,
- Version = "external"
- };
- }
-
- var version = downloadInfo.Version;
-
- if (string.Equals(version, "0", StringComparison.OrdinalIgnoreCase))
- {
- return new FFMpegInfo();
- }
-
- var rootEncoderPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg");
- var versionedDirectoryPath = Path.Combine(rootEncoderPath, version);
-
- var info = new FFMpegInfo
- {
- ProbePath = Path.Combine(versionedDirectoryPath, downloadInfo.FFProbeFilename),
- EncoderPath = Path.Combine(versionedDirectoryPath, downloadInfo.FFMpegFilename),
- Version = version
- };
-
- Directory.CreateDirectory(versionedDirectoryPath);
-
- var excludeFromDeletions = new List { versionedDirectoryPath };
-
- if (!File.Exists(info.ProbePath) || !File.Exists(info.EncoderPath))
- {
- // ffmpeg not present. See if there's an older version we can start with
- var existingVersion = GetExistingVersion(info, rootEncoderPath);
-
- // No older version. Need to download and block until complete
- if (existingVersion == null)
- {
- return new FFMpegInfo();
- }
- else
- {
- info = existingVersion;
- versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath);
- excludeFromDeletions.Add(versionedDirectoryPath);
- }
- }
-
- // Allow just one of these to be overridden, if desired.
- if (!string.IsNullOrWhiteSpace(customffMpegPath))
- {
- info.EncoderPath = customffMpegPath;
- }
- if (!string.IsNullOrWhiteSpace(customffProbePath))
- {
- info.ProbePath = customffProbePath;
- }
-
- return info;
- }
-
- private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath)
- {
- var encoderFilename = Path.GetFileName(info.EncoderPath);
- var probeFilename = Path.GetFileName(info.ProbePath);
-
- foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath))
- {
- var allFiles = _fileSystem.GetFilePaths(directory, true).ToList();
-
- var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase));
- var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase));
-
- if (!string.IsNullOrWhiteSpace(encoder) &&
- !string.IsNullOrWhiteSpace(probe))
- {
- return new FFMpegInfo
- {
- EncoderPath = encoder,
- ProbePath = probe,
- Version = Path.GetFileName(Path.GetDirectoryName(probe))
- };
- }
- }
-
- return null;
- }
- }
-}
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 5d3f7b171..c8cdb984d 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -20,10 +20,10 @@ namespace Jellyfin.Server
[Option('l', "logdir", Required = false, HelpText = "Path to use for writing log files.")]
public string LogDir { get; set; }
- [Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH. Must be specified along with --ffprobe.")]
+ [Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH.")]
public string FFmpegPath { get; set; }
- [Option("ffprobe", Required = false, HelpText = "Path to external FFprobe executable to use in place of default found in PATH. Must be specified along with --ffmpeg.")]
+ [Option("ffprobe", Required = false, HelpText = "(deprecated) Option has no effect and shall be removed in next release.")]
public string FFprobePath { get; set; }
[Option("service", Required = false, HelpText = "Run as headless service.")]
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 057e43910..d4ac3b7c3 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.System;
namespace MediaBrowser.Controller.MediaEncoding
{
@@ -14,7 +15,7 @@ namespace MediaBrowser.Controller.MediaEncoding
///
public interface IMediaEncoder : ITranscoderSupport
{
- string EncoderLocationType { get; }
+ FFmpegLocation EncoderLocation { get; }
///
/// Gets the encoder path.
@@ -91,7 +92,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// System.String.
string EscapeSubtitleFilterPath(string path);
- void Init();
+ void SetFFmpegPath();
void UpdateEncoderPath(string path, string pathType);
bool SupportsEncoder(string encoder);
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index f725d2c01..3eed891cb 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_processFactory = processFactory;
}
- public (IEnumerable decoders, IEnumerable encoders) Validate(string encoderPath)
+ public (IEnumerable decoders, IEnumerable encoders) GetAvailableCoders(string encoderPath)
{
_logger.LogInformation("Validating media encoder at {EncoderPath}", encoderPath);
@@ -48,6 +48,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (string.IsNullOrWhiteSpace(output))
{
+ if (logOutput)
+ {
+ _logger.LogError("FFmpeg validation: The process returned no result");
+ }
return false;
}
@@ -55,6 +59,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1)
{
+ if (logOutput)
+ {
+ _logger.LogError("FFmpeg validation: avconv instead of ffmpeg is not supported");
+ }
return false;
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 7f29c06b4..292457788 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -3,17 +3,14 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
using MediaBrowser.MediaEncoding.Probing;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Diagnostics;
@@ -22,6 +19,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Encoder
@@ -32,340 +30,223 @@ namespace MediaBrowser.MediaEncoding.Encoder
public class MediaEncoder : IMediaEncoder, IDisposable
{
///
- /// The _logger
+ /// Gets the encoder path.
///
+ /// The encoder path.
+ public string EncoderPath => FFmpegPath;
+
+ ///
+ /// The location of the discovered FFmpeg tool.
+ ///
+ public FFmpegLocation EncoderLocation { get; private set; }
+
private readonly ILogger _logger;
-
- ///
- /// Gets the json serializer.
- ///
- /// The json serializer.
private readonly IJsonSerializer _jsonSerializer;
-
- ///
- /// The _thumbnail resource pool
- ///
- private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
-
- public string FFMpegPath { get; private set; }
-
- public string FFProbePath { get; private set; }
-
+ private string FFmpegPath;
+ private string FFprobePath;
protected readonly IServerConfigurationManager ConfigurationManager;
protected readonly IFileSystem FileSystem;
- protected readonly ILiveTvManager LiveTvManager;
- protected readonly IIsoManager IsoManager;
- protected readonly ILibraryManager LibraryManager;
- protected readonly IChannelManager ChannelManager;
- protected readonly ISessionManager SessionManager;
protected readonly Func SubtitleEncoder;
protected readonly Func MediaSourceManager;
- private readonly IHttpClient _httpClient;
- private readonly IZipClient _zipClient;
private readonly IProcessFactory _processFactory;
-
- private readonly List _runningProcesses = new List();
- private readonly bool _hasExternalEncoder;
- private readonly string _originalFFMpegPath;
- private readonly string _originalFFProbePath;
private readonly int DefaultImageExtractionTimeoutMs;
+ private readonly string StartupOptionFFmpegPath;
+ private readonly string StartupOptionFFprobePath;
+
+ private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
+ private readonly List _runningProcesses = new List();
public MediaEncoder(
ILoggerFactory loggerFactory,
IJsonSerializer jsonSerializer,
- string ffMpegPath,
- string ffProbePath,
- bool hasExternalEncoder,
+ string startupOptionsFFmpegPath,
+ string startupOptionsFFprobePath,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
- ILiveTvManager liveTvManager,
- IIsoManager isoManager,
- ILibraryManager libraryManager,
- IChannelManager channelManager,
- ISessionManager sessionManager,
Func subtitleEncoder,
Func mediaSourceManager,
- IHttpClient httpClient,
- IZipClient zipClient,
IProcessFactory processFactory,
int defaultImageExtractionTimeoutMs)
{
_logger = loggerFactory.CreateLogger(nameof(MediaEncoder));
_jsonSerializer = jsonSerializer;
+ StartupOptionFFmpegPath = startupOptionsFFmpegPath;
+ StartupOptionFFprobePath = startupOptionsFFprobePath;
ConfigurationManager = configurationManager;
FileSystem = fileSystem;
- LiveTvManager = liveTvManager;
- IsoManager = isoManager;
- LibraryManager = libraryManager;
- ChannelManager = channelManager;
- SessionManager = sessionManager;
SubtitleEncoder = subtitleEncoder;
- MediaSourceManager = mediaSourceManager;
- _httpClient = httpClient;
- _zipClient = zipClient;
_processFactory = processFactory;
DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
- FFProbePath = ffProbePath;
- FFMpegPath = ffMpegPath;
- _originalFFProbePath = ffProbePath;
- _originalFFMpegPath = ffMpegPath;
- _hasExternalEncoder = hasExternalEncoder;
}
- public string EncoderLocationType
+ ///
+ /// Run at startup or if the user removes a Custom path from transcode page.
+ /// Sets global variables FFmpegPath.
+ /// Precedence is: Config > CLI > $PATH
+ ///
+ public void SetFFmpegPath()
{
- get
+ // ToDo - Finalise removal of the --ffprobe switch
+ if (!string.IsNullOrEmpty(StartupOptionFFprobePath))
{
- if (_hasExternalEncoder)
- {
- return "External";
- }
-
- if (string.IsNullOrWhiteSpace(FFMpegPath))
- {
- return null;
- }
-
- if (IsSystemInstalledPath(FFMpegPath))
- {
- return "System";
- }
-
- return "Custom";
- }
- }
-
- private bool IsSystemInstalledPath(string path)
- {
- if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1)
- {
- return true;
+ _logger.LogWarning("--ffprobe switch is deprecated and shall be removed in the next release");
}
- return false;
- }
-
- public void Init()
- {
- InitPaths();
-
- if (!string.IsNullOrWhiteSpace(FFMpegPath))
+ // 1) Custom path stored in config/encoding xml file under tag takes precedence
+ if (!ValidatePath(ConfigurationManager.GetConfiguration("encoding").EncoderAppPath, FFmpegLocation.Custom))
{
- var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
+ // 2) Check if the --ffmpeg CLI switch has been given
+ if (!ValidatePath(StartupOptionFFmpegPath, FFmpegLocation.SetByArgument))
+ {
+ // 3) Search system $PATH environment variable for valid FFmpeg
+ if (!ValidatePath(ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System))
+ {
+ EncoderLocation = FFmpegLocation.NotFound;
+ FFmpegPath = null;
+ }
+ }
+ }
+
+ // Write the FFmpeg path to the config/encoding.xml file as so it appears in UI
+ var config = ConfigurationManager.GetConfiguration("encoding");
+ config.EncoderAppPathDisplay = FFmpegPath ?? string.Empty;
+ ConfigurationManager.SaveConfiguration("encoding", config);
+
+ // Only if mpeg path is set, try and set path to probe
+ if (FFmpegPath != null)
+ {
+ // Determine a probe path from the mpeg path
+ FFprobePath = Regex.Replace(FFmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1");
+
+ // Interrogate to understand what coders are supported
+ var result = new EncoderValidator(_logger, _processFactory).GetAvailableCoders(FFmpegPath);
SetAvailableDecoders(result.decoders);
SetAvailableEncoders(result.encoders);
}
+
+ _logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation.ToString(), FFmpegPath ?? string.Empty);
}
- private void InitPaths()
- {
- ConfigureEncoderPaths();
-
- if (_hasExternalEncoder)
- {
- LogPaths();
- return;
- }
-
- // If the path was passed in, save it into config now.
- var encodingOptions = GetEncodingOptions();
- var appPath = encodingOptions.EncoderAppPath;
-
- var valueToSave = FFMpegPath;
-
- if (!string.IsNullOrWhiteSpace(valueToSave))
- {
- // if using system variable, don't save this.
- if (IsSystemInstalledPath(valueToSave) || _hasExternalEncoder)
- {
- valueToSave = null;
- }
- }
-
- if (!string.Equals(valueToSave, appPath, StringComparison.Ordinal))
- {
- encodingOptions.EncoderAppPath = valueToSave;
- ConfigurationManager.SaveConfiguration("encoding", encodingOptions);
- }
- }
-
+ ///
+ /// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use.
+ /// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here.
+ ///
+ ///
+ ///
public void UpdateEncoderPath(string path, string pathType)
{
- if (_hasExternalEncoder)
- {
- return;
- }
+ string newPath;
_logger.LogInformation("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty);
- Tuple newPaths;
-
- if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase))
- {
- path = "ffmpeg";
-
- newPaths = TestForInstalledVersions();
- }
- else if (string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
-
- if (!File.Exists(path) && !Directory.Exists(path))
- {
- throw new ResourceNotFoundException();
- }
- newPaths = GetEncoderPaths(path);
- }
- else
+ if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Unexpected pathType value");
}
-
- if (string.IsNullOrWhiteSpace(newPaths.Item1))
+ else if (string.IsNullOrWhiteSpace(path))
{
- throw new ResourceNotFoundException("ffmpeg not found");
+ // User had cleared the custom path in UI
+ newPath = string.Empty;
}
- if (string.IsNullOrWhiteSpace(newPaths.Item2))
+ else if (File.Exists(path))
{
- throw new ResourceNotFoundException("ffprobe not found");
+ newPath = path;
+ }
+ else if (Directory.Exists(path))
+ {
+ // Given path is directory, so resolve down to filename
+ newPath = GetEncoderPathFromDirectory(path, "ffmpeg");
+ }
+ else
+ {
+ throw new ResourceNotFoundException();
}
- path = newPaths.Item1;
-
- if (!ValidateVersion(path, true))
- {
- throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
- }
-
- var config = GetEncodingOptions();
- config.EncoderAppPath = path;
+ // Write the new ffmpeg path to the xml as
+ // This ensures its not lost on next startup
+ var config = ConfigurationManager.GetConfiguration("encoding");
+ config.EncoderAppPath = newPath;
ConfigurationManager.SaveConfiguration("encoding", config);
- Init();
+ // Trigger SetFFmpegPath so we validate the new path and setup probe path
+ SetFFmpegPath();
}
- private bool ValidateVersion(string path, bool logOutput)
+ ///
+ /// Validates the supplied FQPN to ensure it is a ffmpeg utility.
+ /// If checks pass, global variable FFmpegPath and EncoderLocation are updated.
+ ///
+ /// FQPN to test
+ /// Location (External, Custom, System) of tool
+ ///
+ private bool ValidatePath(string path, FFmpegLocation location)
{
- return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
- }
+ bool rc = false;
- private void ConfigureEncoderPaths()
- {
- if (_hasExternalEncoder)
+ if (!string.IsNullOrEmpty(path))
{
- return;
- }
-
- var appPath = GetEncodingOptions().EncoderAppPath;
-
- if (string.IsNullOrWhiteSpace(appPath))
- {
- appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg");
- }
-
- var newPaths = GetEncoderPaths(appPath);
- if (string.IsNullOrWhiteSpace(newPaths.Item1) || string.IsNullOrWhiteSpace(newPaths.Item2) || IsSystemInstalledPath(appPath))
- {
- newPaths = TestForInstalledVersions();
- }
-
- if (!string.IsNullOrWhiteSpace(newPaths.Item1) && !string.IsNullOrWhiteSpace(newPaths.Item2))
- {
- FFMpegPath = newPaths.Item1;
- FFProbePath = newPaths.Item2;
- }
-
- LogPaths();
- }
-
- private Tuple GetEncoderPaths(string configuredPath)
- {
- var appPath = configuredPath;
-
- if (!string.IsNullOrWhiteSpace(appPath))
- {
- if (Directory.Exists(appPath))
+ if (File.Exists(path))
{
- return GetPathsFromDirectory(appPath);
+ rc = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, true);
+
+ if (!rc)
+ {
+ _logger.LogWarning("FFmpeg: {0}: Failed version check: {1}", location.ToString(), path);
+ }
+
+ // ToDo - Enable the ffmpeg validator. At the moment any version can be used.
+ rc = true;
+
+ FFmpegPath = path;
+ EncoderLocation = location;
}
-
- if (File.Exists(appPath))
+ else
{
- return new Tuple(appPath, GetProbePathFromEncoderPath(appPath));
+ _logger.LogWarning("FFmpeg: {0}: File not found: {1}", location.ToString(), path);
}
}
- return new Tuple(null, null);
+ return rc;
}
- private Tuple TestForInstalledVersions()
+ private string GetEncoderPathFromDirectory(string path, string filename)
{
- string encoderPath = null;
- string probePath = null;
-
- if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true))
+ try
{
- encoderPath = _originalFFMpegPath;
- probePath = _originalFFProbePath;
+ var files = FileSystem.GetFilePaths(path);
+
+ var excludeExtensions = new[] { ".c" };
+
+ return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), filename, StringComparison.OrdinalIgnoreCase)
+ && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
}
-
- if (string.IsNullOrWhiteSpace(encoderPath))
+ catch (Exception)
{
- if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false))
+ // Trap all exceptions, like DirNotExists, and return null
+ return null;
+ }
+ }
+
+ ///
+ /// Search the system $PATH environment variable looking for given filename.
+ ///
+ ///
+ ///
+ private string ExistsOnSystemPath(string filename)
+ {
+ var values = Environment.GetEnvironmentVariable("PATH");
+
+ foreach (var path in values.Split(Path.PathSeparator))
+ {
+ var candidatePath = GetEncoderPathFromDirectory(path, filename);
+
+ if (!string.IsNullOrEmpty(candidatePath))
{
- encoderPath = "ffmpeg";
- probePath = "ffprobe";
+ return candidatePath;
}
}
-
- return new Tuple(encoderPath, probePath);
- }
-
- private Tuple GetPathsFromDirectory(string path)
- {
- // Since we can't predict the file extension, first try directly within the folder
- // If that doesn't pan out, then do a recursive search
- var files = FileSystem.GetFilePaths(path);
-
- var excludeExtensions = new[] { ".c" };
-
- var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
- var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
-
- if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath))
- {
- files = FileSystem.GetFilePaths(path, true);
-
- ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
-
- if (!string.IsNullOrWhiteSpace(ffmpegPath))
- {
- ffprobePath = GetProbePathFromEncoderPath(ffmpegPath);
- }
- }
-
- return new Tuple(ffmpegPath, ffprobePath);
- }
-
- private string GetProbePathFromEncoderPath(string appPath)
- {
- return FileSystem.GetFilePaths(Path.GetDirectoryName(appPath))
- .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
- }
-
- private void LogPaths()
- {
- _logger.LogInformation("FFMpeg: {0}", FFMpegPath ?? "not found");
- _logger.LogInformation("FFProbe: {0}", FFProbePath ?? "not found");
- }
-
- private EncodingOptions GetEncodingOptions()
- {
- return ConfigurationManager.GetConfiguration("encoding");
+ return null;
}
private List _encoders = new List();
@@ -412,12 +293,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
return true;
}
- ///
- /// Gets the encoder path.
- ///
- /// The encoder path.
- public string EncoderPath => FFMpegPath;
-
///
/// Gets the media info.
///
@@ -489,7 +364,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
RedirectStandardOutput = true,
- FileName = FFProbePath,
+ FileName = FFprobePath,
Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
IsHidden = true,
@@ -691,7 +566,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
CreateNoWindow = true,
UseShellExecute = false,
- FileName = FFMpegPath,
+ FileName = FFmpegPath,
Arguments = args,
IsHidden = true,
ErrorDialog = false,
@@ -814,7 +689,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
CreateNoWindow = true,
UseShellExecute = false,
- FileName = FFMpegPath,
+ FileName = FFmpegPath,
Arguments = args,
IsHidden = true,
ErrorDialog = false,
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 8584bd3dd..285ff4ba5 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -8,7 +8,14 @@ namespace MediaBrowser.Model.Configuration
public bool EnableThrottling { get; set; }
public int ThrottleDelaySeconds { get; set; }
public string HardwareAccelerationType { get; set; }
+ ///
+ /// FFmpeg path as set by the user via the UI
+ ///
public string EncoderAppPath { get; set; }
+ ///
+ /// The current FFmpeg path being used by the system and displayed on the transcode page
+ ///
+ public string EncoderAppPathDisplay { get; set; }
public string VaapiDevice { get; set; }
public int H264Crf { get; set; }
public string H264Preset { get; set; }
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index 581a1069c..6482f2c84 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -4,6 +4,21 @@ using MediaBrowser.Model.Updates;
namespace MediaBrowser.Model.System
{
+ ///
+ /// Enum describing the location of the FFmpeg tool.
+ ///
+ public enum FFmpegLocation
+ {
+ /// No path to FFmpeg found.
+ NotFound,
+ /// Path supplied via command line using switch --ffmpeg.
+ SetByArgument,
+ /// User has supplied path via Transcoding UI page.
+ Custom,
+ /// FFmpeg tool found on system $PATH.
+ System
+ };
+
///
/// Class SystemInfo
///
@@ -122,7 +137,7 @@ namespace MediaBrowser.Model.System
/// true if this instance has update available; otherwise, false.
public bool HasUpdateAvailable { get; set; }
- public string EncoderLocationType { get; set; }
+ public FFmpegLocation EncoderLocation { get; set; }
public Architecture SystemArchitecture { get; set; }