diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 94d2cd5da..2c0d0e746 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;
@@ -792,7 +791,8 @@ 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);
@@ -908,83 +908,25 @@ 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)
+ private void RegisterMediaEncoder(IAssemblyInfo assemblyInfo)
{
- 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(
+ MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
LoggerFactory,
JsonSerializer,
- encoderPath,
- probePath,
- hasExternalEncoder,
+ StartupOptions.FFmpegPath,
+ StartupOptions.FFprobePath,
ServerConfigurationManager,
FileSystemManager,
- LiveTvManager,
- IsoManager,
- LibraryManager,
- ChannelManager,
- SessionManager,
() => SubtitleEncoder,
() => MediaSourceManager,
- HttpClient,
- ZipClient,
ProcessFactory,
5000);
- MediaEncoder = mediaEncoder;
- serviceCollection.AddSingleton(MediaEncoder);
+ RegisterSingleInstance(MediaEncoder);
}
///
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/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index f725d2c01..1eeea87a0 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);
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 7f29c06b4..36d72cad9 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -7,13 +7,9 @@ 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;
@@ -32,323 +28,288 @@ namespace MediaBrowser.MediaEncoding.Encoder
public class MediaEncoder : IMediaEncoder, IDisposable
{
///
- /// The _logger
+ /// Gets the encoder path.
///
+ /// The encoder path.
+ public string EncoderPath => FFmpegPath;
+
+ ///
+ /// External: path supplied via command line
+ /// Custom: coming from UI or config/encoding.xml file
+ /// System: FFmpeg found in system $PATH
+ /// null: No FFmpeg found
+ ///
+ public string EncoderLocationType { 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 { get; set; }
+ private string FFprobePath { get; set; }
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 and EncoderLocationType.
+ /// If startup options --ffprobe is given then FFprobePath is set too.
+ ///
+ public void Init()
{
- get
+ // 1) If given, use the --ffmpeg CLI switch
+ if (ValidatePathFFmpeg("From CLI Switch", StartupOptionFFmpegPath))
{
- if (_hasExternalEncoder)
+ _logger.LogInformation("FFmpeg: Using path from command line switch --ffmpeg");
+ EncoderLocationType = "External";
+ }
+
+ // 2) Try Custom path stroed in config/encoding xml file under tag
+ else if (ValidatePathFFmpeg("From Config File", ConfigurationManager.GetConfiguration("encoding").EncoderAppPathCustom))
+ {
+ _logger.LogInformation("FFmpeg: Using path from config/encoding.xml file");
+ EncoderLocationType = "Custom";
+ }
+
+ // 3) Search system $PATH environment variable for valid FFmpeg
+ else if (ValidatePathFFmpeg("From $PATH", ExistsOnSystemPath("ffmpeg")))
+ {
+ _logger.LogInformation("FFmpeg: Using system $PATH for FFmpeg");
+ EncoderLocationType = "System";
+ }
+ else
+ {
+ _logger.LogError("FFmpeg: No suitable executable found");
+ FFmpegPath = null;
+ EncoderLocationType = null;
+ }
+
+ // If given, use the --ffprobe CLI switch
+ if (ValidatePathFFprobe("CLI Switch", StartupOptionFFprobePath))
+ {
+ _logger.LogInformation("FFprobe: Using path from command line switch --ffprobe");
+ }
+ else
+ {
+ // FFprobe path from command line is no good, so set to null and let ReInit() try
+ // and set using the FFmpeg path.
+ FFprobePath = null;
+ }
+
+ ReInit();
+ }
+
+ ///
+ /// Writes the currently used FFmpeg to config/encoding.xml file.
+ /// Sets the FFprobe path if not currently set.
+ /// Interrogates the FFmpeg tool to identify what encoders/decodres are available.
+ ///
+ private void ReInit()
+ {
+ // Write the FFmpeg path to the config/encoding.xml file so it appears in UI
+ var config = ConfigurationManager.GetConfiguration("encoding");
+ config.EncoderAppPath = FFmpegPath ?? string.Empty;
+ ConfigurationManager.SaveConfiguration("encoding", config);
+
+ // Only if mpeg path is set, try and set path to probe
+ if (FFmpegPath != null)
+ {
+ // Probe would be null here if no valid --ffprobe path was given
+ // at startup, or we're performing ReInit following mpeg path update from UI
+ if (FFprobePath == null)
{
- return "External";
+ // Use the mpeg path to create a probe path
+ if (ValidatePathFFprobe("Copied from FFmpeg:", GetProbePathFromEncoderPath(FFmpegPath)))
+ {
+ _logger.LogInformation("FFprobe: Using FFprobe in same folders as FFmpeg");
+ }
+ else
+ {
+ _logger.LogError("FFprobe: No suitable executable found");
+ }
}
- if (string.IsNullOrWhiteSpace(FFMpegPath))
- {
- return null;
- }
+ // Interrogate to understand what coders it supports
+ var result = new EncoderValidator(_logger, _processFactory).GetAvailableCoders(FFmpegPath);
- if (IsSystemInstalledPath(FFMpegPath))
- {
- return "System";
- }
+ SetAvailableDecoders(result.decoders);
+ SetAvailableEncoders(result.encoders);
+ }
- return "Custom";
+ // Stamp FFmpeg paths to the log file
+ LogPaths();
+ }
+
+ ///
+ /// Triggered from the Settings > Trascoding UI page when users sumits Custom FFmpeg path to use.
+ ///
+ ///
+ ///
+ public void UpdateEncoderPath(string path, string pathType)
+ {
+ _logger.LogInformation("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty);
+
+ if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ArgumentException("Unexpected pathType value");
+ }
+ else
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ // User had cleared the cutom path in UI. Clear the Custom config
+ // setting and peform full Init to relook any CLI switches and system $PATH
+ var config = ConfigurationManager.GetConfiguration("encoding");
+ config.EncoderAppPathCustom = string.Empty;
+ ConfigurationManager.SaveConfiguration("encoding", config);
+
+ Init();
+ }
+ else if (!File.Exists(path) && !Directory.Exists(path))
+ {
+ // Given path is neither file or folder
+ throw new ResourceNotFoundException();
+ }
+ else
+ {
+ // Supplied path could be either file path or folder path.
+ // Resolve down to file path and validate
+ path = GetEncoderPath(path);
+
+ if (path == null)
+ {
+ throw new ResourceNotFoundException("FFmpeg not found");
+ }
+ else if (!ValidatePathFFmpeg("New From UI", path))
+ {
+ throw new ResourceNotFoundException("Failed validation checks. Version 4.0 or greater is required");
+ }
+ else
+ {
+ EncoderLocationType = "Custom";
+
+ // Write the validated mpeg path to the xml as
+ // This ensures its not lost on new startup
+ var config = ConfigurationManager.GetConfiguration("encoding");
+ config.EncoderAppPathCustom = FFmpegPath;
+ ConfigurationManager.SaveConfiguration("encoding", config);
+
+ FFprobePath = null; // Clear probe path so it gets relooked in ReInit()
+
+ ReInit();
+ }
+ }
}
}
- private bool IsSystemInstalledPath(string path)
+ private bool ValidatePath(string type, string path)
{
- if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1)
+ if (!string.IsNullOrEmpty(path))
{
+ if (File.Exists(path))
+ {
+ var valid = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, true);
+
+ if (valid == true)
+ {
+ return true;
+ }
+ else
+ {
+ _logger.LogError("{0}: Failed validation checks. Version 4.0 or greater is required: {1}", type, path);
+ }
+ }
+ else
+ {
+ _logger.LogError("{0}: File not found: {1}", type, path);
+ }
+ }
+
+ return false;
+ }
+
+ private bool ValidatePathFFmpeg(string comment, string path)
+ {
+ if (ValidatePath("FFmpeg: " + comment, path) == true)
+ {
+ FFmpegPath = path;
return true;
}
return false;
}
- public void Init()
+ private bool ValidatePathFFprobe(string comment, string path)
{
- InitPaths();
-
- if (!string.IsNullOrWhiteSpace(FFMpegPath))
+ if (ValidatePath("FFprobe: " + comment, path) == true)
{
- var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
-
- SetAvailableDecoders(result.decoders);
- SetAvailableEncoders(result.encoders);
+ FFprobePath = path;
+ return true;
}
+
+ return false;
}
- private void InitPaths()
+ private string GetEncoderPath(string path)
{
- ConfigureEncoderPaths();
-
- if (_hasExternalEncoder)
+ if (Directory.Exists(path))
{
- LogPaths();
- return;
+ return GetEncoderPathFromDirectory(path);
}
- // 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 (File.Exists(path))
{
- // if using system variable, don't save this.
- if (IsSystemInstalledPath(valueToSave) || _hasExternalEncoder)
- {
- valueToSave = null;
- }
+ return path;
}
- if (!string.Equals(valueToSave, appPath, StringComparison.Ordinal))
- {
- encodingOptions.EncoderAppPath = valueToSave;
- ConfigurationManager.SaveConfiguration("encoding", encodingOptions);
- }
+ return null;
}
- public void UpdateEncoderPath(string path, string pathType)
+ private string GetEncoderPathFromDirectory(string path)
{
- if (_hasExternalEncoder)
+ try
{
- return;
+ var files = FileSystem.GetFilePaths(path);
+
+ var excludeExtensions = new[] { ".c" };
+
+ return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
}
-
- _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))
+ catch (Exception)
{
- path = "ffmpeg";
-
- newPaths = TestForInstalledVersions();
+ // Trap all exceptions, like DirNotExists, and return null
+ return null;
}
- 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
- {
- throw new ArgumentException("Unexpected pathType value");
- }
-
- if (string.IsNullOrWhiteSpace(newPaths.Item1))
- {
- throw new ResourceNotFoundException("ffmpeg not found");
- }
- if (string.IsNullOrWhiteSpace(newPaths.Item2))
- {
- throw new ResourceNotFoundException("ffprobe not found");
- }
-
- path = newPaths.Item1;
-
- if (!ValidateVersion(path, true))
- {
- throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
- }
-
- var config = GetEncodingOptions();
- config.EncoderAppPath = path;
- ConfigurationManager.SaveConfiguration("encoding", config);
-
- Init();
- }
-
- private bool ValidateVersion(string path, bool logOutput)
- {
- return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
- }
-
- private void ConfigureEncoderPaths()
- {
- if (_hasExternalEncoder)
- {
- 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))
- {
- return GetPathsFromDirectory(appPath);
- }
-
- if (File.Exists(appPath))
- {
- return new Tuple(appPath, GetProbePathFromEncoderPath(appPath));
- }
- }
-
- return new Tuple(null, null);
- }
-
- private Tuple TestForInstalledVersions()
- {
- string encoderPath = null;
- string probePath = null;
-
- if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true))
- {
- encoderPath = _originalFFMpegPath;
- probePath = _originalFFProbePath;
- }
-
- if (string.IsNullOrWhiteSpace(encoderPath))
- {
- if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false))
- {
- encoderPath = "ffmpeg";
- probePath = "ffprobe";
- }
- }
-
- 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)
@@ -357,15 +318,31 @@ namespace MediaBrowser.MediaEncoding.Encoder
.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
}
- private void LogPaths()
+ ///
+ /// Search the system $PATH environment variable looking for given filename.
+ ///
+ ///
+ ///
+ private string ExistsOnSystemPath(string fileName)
{
- _logger.LogInformation("FFMpeg: {0}", FFMpegPath ?? "not found");
- _logger.LogInformation("FFProbe: {0}", FFProbePath ?? "not found");
+ var values = Environment.GetEnvironmentVariable("PATH");
+
+ foreach (var path in values.Split(Path.PathSeparator))
+ {
+ var candidatePath = GetEncoderPathFromDirectory(path);
+
+ if (ValidatePath("Found on PATH", candidatePath))
+ {
+ return candidatePath;
+ }
+ }
+ return null;
}
- private EncodingOptions GetEncodingOptions()
+ private void LogPaths()
{
- return ConfigurationManager.GetConfiguration("encoding");
+ _logger.LogInformation("FFMpeg: {0}", FFmpegPath ?? "not found");
+ _logger.LogInformation("FFProbe: {0}", FFprobePath ?? "not found");
}
private List _encoders = new List();
@@ -412,12 +389,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
return true;
}
- ///
- /// Gets the encoder path.
- ///
- /// The encoder path.
- public string EncoderPath => FFMpegPath;
-
///
/// Gets the media info.
///
@@ -489,7 +460,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 +662,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
CreateNoWindow = true,
UseShellExecute = false,
- FileName = FFMpegPath,
+ FileName = FFmpegPath,
Arguments = args,
IsHidden = true,
ErrorDialog = false,
@@ -814,7 +785,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..ff697437a 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -8,7 +8,8 @@ namespace MediaBrowser.Model.Configuration
public bool EnableThrottling { get; set; }
public int ThrottleDelaySeconds { get; set; }
public string HardwareAccelerationType { get; set; }
- public string EncoderAppPath { get; set; }
+ public string EncoderAppPathCustom { 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
public string VaapiDevice { get; set; }
public int H264Crf { get; set; }
public string H264Preset { get; set; }