From 168587b2a0d1f495a9f85e05b1e518f615b0cfcc Mon Sep 17 00:00:00 2001 From: Sven Van den brande Date: Sun, 27 Mar 2016 23:11:27 +0200 Subject: Remove unused code... --- MediaBrowser.ServerApplication/Native/WindowsApp.cs | 1 - 1 file changed, 1 deletion(-) (limited to 'MediaBrowser.ServerApplication/Native/WindowsApp.cs') diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index ac07a369d..164037dc5 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Net; -using MediaBrowser.IsoMounter; using MediaBrowser.Model.Logging; using MediaBrowser.Server.Startup.Common; using MediaBrowser.ServerApplication.Networking; -- cgit v1.2.3 From d91c63c55324df395743d6104ba18a27ace01231 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 2 Apr 2016 00:29:48 -0400 Subject: update ffmpeg info --- MediaBrowser.Server.Mono/Native/BaseMonoApp.cs | 95 +++++ .../ApplicationHost.cs | 2 +- .../FFMpeg/FFMpegDownloadInfo.cs | 136 ------ .../FFMpeg/FFMpegDownloader.cs | 458 -------------------- .../FFMpeg/FFMpegInstallInfo.cs | 21 + .../FFMpeg/FFMpegLoader.cs | 460 +++++++++++++++++++++ MediaBrowser.Server.Startup.Common/INativeApp.cs | 3 + .../MediaBrowser.Server.Startup.Common.csproj | 4 +- .../Native/WindowsApp.cs | 30 +- 9 files changed, 611 insertions(+), 598 deletions(-) delete mode 100644 MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs delete mode 100644 MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs create mode 100644 MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs create mode 100644 MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs (limited to 'MediaBrowser.ServerApplication/Native/WindowsApp.cs') diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs index e54bc0b4a..6d19c3275 100644 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs @@ -9,6 +9,8 @@ using System.Collections.Generic; using System.Reflection; using System.Text.RegularExpressions; using MediaBrowser.Controller.Power; +using MediaBrowser.Server.Startup.Common.FFMpeg; +using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; namespace MediaBrowser.Server.Mono.Native { @@ -209,6 +211,99 @@ namespace MediaBrowser.Server.Mono.Native { return new NullPowerManagement(); } + + public FFMpegInstallInfo GetFfmpegInstallInfo() + { + return GetInfo(Environment); + } + + public static FFMpegInstallInfo GetInfo(NativeEnvironment environment) + { + 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/ + + switch (environment.OperatingSystem) + { + case OperatingSystem.Bsd: + break; + case OperatingSystem.Linux: + + info.ArchiveType = "7z"; + info.Version = "20160215"; + break; + case OperatingSystem.Osx: + + info.ArchiveType = "7z"; + + switch (environment.SystemArchitecture) + { + case Architecture.X86_X64: + info.Version = "20160124"; + break; + case Architecture.X86: + info.Version = "20150110"; + break; + } + break; + } + + info.DownloadUrls = GetDownloadUrls(environment); + + return info; + } + + private static string[] GetDownloadUrls(NativeEnvironment environment) + { + switch (environment.OperatingSystem) + { + case OperatingSystem.Osx: + + switch (environment.SystemArchitecture) + { + case Architecture.X86_X64: + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z" + }; + case Architecture.X86: + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x86-2.5.3.7z" + }; + } + break; + + case OperatingSystem.Linux: + + switch (environment.SystemArchitecture) + { + case Architecture.X86_X64: + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z" + }; + case Architecture.X86: + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z" + }; + case Architecture.Arm: + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-arm.7z" + }; + } + break; + } + + // No version available + return new string[] { }; + } + } public class NullPowerManagement : IPowerManagement diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index b5988b18a..93dbe2945 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -618,7 +618,7 @@ namespace MediaBrowser.Server.Startup.Common /// Task. private async Task RegisterMediaEncoder(IProgress progress) { - var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment, NativeApp.GetType().Assembly) + var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment, NativeApp.GetType().Assembly, NativeApp.GetFfmpegInstallInfo()) .GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false); var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs deleted file mode 100644 index 4979ff82b..000000000 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs +++ /dev/null @@ -1,136 +0,0 @@ - -namespace MediaBrowser.Server.Startup.Common.FFMpeg -{ - public class FFMpegDownloadInfo - { - public string Version { get; set; } - public string FFMpegFilename { get; set; } - public string FFProbeFilename { get; set; } - public string ArchiveType { get; set; } - public string[] DownloadUrls { get; set; } - public bool IsEmbedded { get; set; } - - public FFMpegDownloadInfo() - { - DownloadUrls = new string[] { }; - Version = "Path"; - FFMpegFilename = "ffmpeg"; - FFProbeFilename = "ffprobe"; - } - - public static FFMpegDownloadInfo GetInfo(NativeEnvironment environment) - { - var info = new FFMpegDownloadInfo(); - - // 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/ - - switch (environment.OperatingSystem) - { - case OperatingSystem.Bsd: - break; - case OperatingSystem.Linux: - - info.ArchiveType = "7z"; - info.Version = "20160215"; - break; - case OperatingSystem.Osx: - - info.ArchiveType = "7z"; - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - info.Version = "20160124"; - break; - case Architecture.X86: - info.Version = "20150110"; - break; - } - break; - - case OperatingSystem.Windows: - - info.FFMpegFilename = "ffmpeg.exe"; - info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20160401"; - info.ArchiveType = "7z"; - info.IsEmbedded = true; - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - break; - case Architecture.X86: - break; - } - break; - } - - info.DownloadUrls = GetDownloadUrls(environment); - - return info; - } - - private static string[] GetDownloadUrls(NativeEnvironment environment) - { - switch (environment.OperatingSystem) - { - case OperatingSystem.Windows: - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - return new string[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx64.7z" }; - case Architecture.X86: - return new string[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx86.7z" }; - } - break; - - case OperatingSystem.Osx: - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z" - }; - case Architecture.X86: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x86-2.5.3.7z" - }; - } - break; - - case OperatingSystem.Linux: - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z" - }; - case Architecture.X86: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z" - }; - case Architecture.Arm: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-arm.7z" - }; - } - break; - } - - // No version available - return new string[] { }; - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs deleted file mode 100644 index c538b81a4..000000000 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs +++ /dev/null @@ -1,458 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using Mono.Unix.Native; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using CommonIO; - -namespace MediaBrowser.Server.Startup.Common.FFMpeg -{ - public class FFMpegDownloader - { - private readonly IHttpClient _httpClient; - private readonly IApplicationPaths _appPaths; - private readonly ILogger _logger; - private readonly IZipClient _zipClient; - private readonly IFileSystem _fileSystem; - private readonly NativeEnvironment _environment; - private Assembly _ownerAssembly; - - private readonly string[] _fontUrls = - { - "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z" - }; - - public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, NativeEnvironment environment, Assembly ownerAssembly) - { - _logger = logger; - _appPaths = appPaths; - _httpClient = httpClient; - _zipClient = zipClient; - _fileSystem = fileSystem; - _environment = environment; - _ownerAssembly = ownerAssembly; - } - - public async Task GetFFMpegInfo(NativeEnvironment environment, StartupOptions options, IProgress progress) - { - var customffMpegPath = options.GetOption("-ffmpeg"); - var customffProbePath = options.GetOption("-ffprobe"); - - if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath)) - { - return new FFMpegInfo - { - ProbePath = customffProbePath, - EncoderPath = customffMpegPath, - Version = "custom" - }; - } - - var downloadInfo = FFMpegDownloadInfo.GetInfo(environment); - - var version = downloadInfo.Version; - - if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase)) - { - return new FFMpegInfo - { - ProbePath = downloadInfo.FFProbeFilename, - EncoderPath = downloadInfo.FFMpegFilename, - Version = version - }; - } - - 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 - }; - - _fileSystem.CreateDirectory(versionedDirectoryPath); - - var excludeFromDeletions = new List { versionedDirectoryPath }; - - if (!_fileSystem.FileExists(info.ProbePath) || !_fileSystem.FileExists(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) - { - await DownloadFFMpeg(downloadInfo, versionedDirectoryPath, progress).ConfigureAwait(false); - } - else - { - // Older version found. - // Start with that. Download new version in the background. - var newPath = versionedDirectoryPath; - Task.Run(() => DownloadFFMpegInBackground(downloadInfo, newPath)); - - info = existingVersion; - versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath); - - excludeFromDeletions.Add(versionedDirectoryPath); - } - } - - if (_environment.OperatingSystem == OperatingSystem.Windows) - { - await DownloadFonts(versionedDirectoryPath).ConfigureAwait(false); - } - - DeleteOlderFolders(Path.GetDirectoryName(versionedDirectoryPath), excludeFromDeletions); - - // Allow just one of these to be overridden, if desired. - if (!string.IsNullOrWhiteSpace(customffMpegPath)) - { - info.EncoderPath = customffMpegPath; - } - if (!string.IsNullOrWhiteSpace(customffProbePath)) - { - info.EncoderPath = customffProbePath; - } - - return info; - } - - private void DeleteOlderFolders(string path, IEnumerable excludeFolders) - { - var folders = Directory.GetDirectories(path) - .Where(i => !excludeFolders.Contains(i, StringComparer.OrdinalIgnoreCase)) - .ToList(); - - foreach (var folder in folders) - { - DeleteFolder(folder); - } - } - - private void DeleteFolder(string path) - { - try - { - _fileSystem.DeleteDirectory(path, true); - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting {0}", ex, path); - } - } - - private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath) - { - var encoderFilename = Path.GetFileName(info.EncoderPath); - var probeFilename = Path.GetFileName(info.ProbePath); - - foreach (var directory in Directory.EnumerateDirectories(rootEncoderPath, "*", SearchOption.TopDirectoryOnly) - .ToList()) - { - var allFiles = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).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; - } - - private async void DownloadFFMpegInBackground(FFMpegDownloadInfo downloadinfo, string directory) - { - try - { - await DownloadFFMpeg(downloadinfo, directory, new Progress()).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading ffmpeg", ex); - } - } - - private async Task DownloadFFMpeg(FFMpegDownloadInfo downloadinfo, string directory, IProgress progress) - { - if (downloadinfo.IsEmbedded) - { - var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); - _fileSystem.CreateDirectory(Path.GetDirectoryName(tempFile)); - - using (var stream = _ownerAssembly.GetManifestResourceStream(downloadinfo.DownloadUrls[0])) - { - using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) - { - await stream.CopyToAsync(fs).ConfigureAwait(false); - } - } - ExtractFFMpeg(downloadinfo, tempFile, directory); - } - - foreach (var url in downloadinfo.DownloadUrls) - { - progress.Report(0); - - try - { - var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions - { - Url = url, - CancellationToken = CancellationToken.None, - Progress = progress - - }).ConfigureAwait(false); - - ExtractFFMpeg(downloadinfo, tempFile, directory); - return; - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading {0}", ex, url); - } - } - - if (downloadinfo.DownloadUrls.Length == 0) - { - throw new ApplicationException("ffmpeg unvailable. Please install it and start the server with two command line arguments: -ffmpeg \"{PATH}\" and -ffprobe \"{PATH}\""); - } - - throw new ApplicationException("Unable to download required components. Please try again later."); - } - - private void ExtractFFMpeg(FFMpegDownloadInfo downloadinfo, string tempFile, string targetFolder) - { - _logger.Info("Extracting ffmpeg from {0}", tempFile); - - var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); - - _fileSystem.CreateDirectory(tempFolder); - - try - { - ExtractArchive(downloadinfo, tempFile, tempFolder); - - var files = Directory.EnumerateFiles(tempFolder, "*", SearchOption.AllDirectories) - .ToList(); - - foreach (var file in files.Where(i => - { - var filename = Path.GetFileName(i); - - return - string.Equals(filename, downloadinfo.FFProbeFilename, StringComparison.OrdinalIgnoreCase) || - string.Equals(filename, downloadinfo.FFMpegFilename, StringComparison.OrdinalIgnoreCase); - })) - { - var targetFile = Path.Combine(targetFolder, Path.GetFileName(file)); - _fileSystem.CopyFile(file, targetFile, true); - SetFilePermissions(targetFile); - } - } - finally - { - DeleteFile(tempFile); - } - } - - private void SetFilePermissions(string path) - { - // Linux: File permission to 666, and user's execute bit - if (_environment.OperatingSystem == OperatingSystem.Bsd || _environment.OperatingSystem == OperatingSystem.Linux || _environment.OperatingSystem == OperatingSystem.Osx) - { - _logger.Info("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path); - - Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH); - } - } - - private void ExtractArchive(FFMpegDownloadInfo downloadinfo, string archivePath, string targetPath) - { - _logger.Info("Extracting {0} to {1}", archivePath, targetPath); - - if (string.Equals(downloadinfo.ArchiveType, "7z", StringComparison.OrdinalIgnoreCase)) - { - _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); - } - else if (string.Equals(downloadinfo.ArchiveType, "gz", StringComparison.OrdinalIgnoreCase)) - { - _zipClient.ExtractAllFromTar(archivePath, targetPath, true); - } - } - private void Extract7zArchive(string archivePath, string targetPath) - { - _logger.Info("Extracting {0} to {1}", archivePath, targetPath); - - _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); - } - - private void DeleteFile(string path) - { - try - { - _fileSystem.DeleteFile(path); - } - catch (IOException ex) - { - _logger.ErrorException("Error deleting temp file {0}", ex, path); - } - } - - /// - /// Extracts the fonts. - /// - /// The target path. - /// Task. - private 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 - Task.Run(async () => - { - await DownloadFontFile(fontsDirectory, fontFilename, new Progress()).ConfigureAwait(false); - - await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); - }); - } - } - catch (HttpException ex) - { - // Don't let the server crash because of this - _logger.ErrorException("Error downloading ffmpeg font files", ex); - } - catch (Exception ex) - { - // Don't let the server crash because of this - _logger.ErrorException("Error writing ffmpeg font files", ex); - } - } - - /// - /// Downloads the font file. - /// - /// The fonts directory. - /// The font filename. - /// Task. - private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress progress) - { - var existingFile = Directory - .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories) - .FirstOrDefault(); - - 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.ErrorException("Error copying file", ex); - } - } - - 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.ErrorException("Failed to download ffmpeg font file from {0}", ex, 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.ErrorException("Error deleting temp file {0}", ex, tempFile); - } - } - - /// - /// Writes the font config file. - /// - /// The fonts directory. - /// Task. - 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("{0}ArialArial Unicode MS", fontsDirectory); - - var bytes = Encoding.UTF8.GetBytes(contents); - - using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileMode.Create, FileAccess.Write, - FileShare.Read, true)) - { - await fileStream.WriteAsync(bytes, 0, bytes.Length); - } - } - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs new file mode 100644 index 000000000..1ce1b55c2 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs @@ -0,0 +1,21 @@ + +namespace MediaBrowser.Server.Startup.Common.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 string[] DownloadUrls { get; set; } + public bool IsEmbedded { get; set; } + + public FFMpegInstallInfo() + { + DownloadUrls = new string[] { }; + Version = "Path"; + FFMpegFilename = "ffmpeg"; + FFProbeFilename = "ffprobe"; + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs new file mode 100644 index 000000000..fd005de86 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs @@ -0,0 +1,460 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; +using Mono.Unix.Native; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using CommonIO; + +namespace MediaBrowser.Server.Startup.Common.FFMpeg +{ + public class FFMpegLoader + { + private readonly IHttpClient _httpClient; + private readonly IApplicationPaths _appPaths; + private readonly ILogger _logger; + private readonly IZipClient _zipClient; + private readonly IFileSystem _fileSystem; + private readonly NativeEnvironment _environment; + private readonly Assembly _ownerAssembly; + private readonly FFMpegInstallInfo _ffmpegInstallInfo; + + private readonly string[] _fontUrls = + { + "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z" + }; + + public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, NativeEnvironment environment, Assembly ownerAssembly, FFMpegInstallInfo ffmpegInstallInfo) + { + _logger = logger; + _appPaths = appPaths; + _httpClient = httpClient; + _zipClient = zipClient; + _fileSystem = fileSystem; + _environment = environment; + _ownerAssembly = ownerAssembly; + _ffmpegInstallInfo = ffmpegInstallInfo; + } + + public async Task GetFFMpegInfo(NativeEnvironment environment, StartupOptions options, IProgress progress) + { + var customffMpegPath = options.GetOption("-ffmpeg"); + var customffProbePath = options.GetOption("-ffprobe"); + + if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath)) + { + return new FFMpegInfo + { + ProbePath = customffProbePath, + EncoderPath = customffMpegPath, + Version = "custom" + }; + } + + var downloadInfo = _ffmpegInstallInfo; + + var version = downloadInfo.Version; + + if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase)) + { + return new FFMpegInfo + { + ProbePath = downloadInfo.FFProbeFilename, + EncoderPath = downloadInfo.FFMpegFilename, + Version = version + }; + } + + 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 + }; + + _fileSystem.CreateDirectory(versionedDirectoryPath); + + var excludeFromDeletions = new List { versionedDirectoryPath }; + + if (!_fileSystem.FileExists(info.ProbePath) || !_fileSystem.FileExists(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) + { + await DownloadFFMpeg(downloadInfo, versionedDirectoryPath, progress).ConfigureAwait(false); + } + else + { + // Older version found. + // Start with that. Download new version in the background. + var newPath = versionedDirectoryPath; + Task.Run(() => DownloadFFMpegInBackground(downloadInfo, newPath)); + + info = existingVersion; + versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath); + + excludeFromDeletions.Add(versionedDirectoryPath); + } + } + + if (_environment.OperatingSystem == OperatingSystem.Windows) + { + await DownloadFonts(versionedDirectoryPath).ConfigureAwait(false); + } + + DeleteOlderFolders(Path.GetDirectoryName(versionedDirectoryPath), excludeFromDeletions); + + // Allow just one of these to be overridden, if desired. + if (!string.IsNullOrWhiteSpace(customffMpegPath)) + { + info.EncoderPath = customffMpegPath; + } + if (!string.IsNullOrWhiteSpace(customffProbePath)) + { + info.EncoderPath = customffProbePath; + } + + return info; + } + + private void DeleteOlderFolders(string path, IEnumerable excludeFolders) + { + var folders = Directory.GetDirectories(path) + .Where(i => !excludeFolders.Contains(i, StringComparer.OrdinalIgnoreCase)) + .ToList(); + + foreach (var folder in folders) + { + DeleteFolder(folder); + } + } + + private void DeleteFolder(string path) + { + try + { + _fileSystem.DeleteDirectory(path, true); + } + catch (Exception ex) + { + _logger.ErrorException("Error deleting {0}", ex, path); + } + } + + private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath) + { + var encoderFilename = Path.GetFileName(info.EncoderPath); + var probeFilename = Path.GetFileName(info.ProbePath); + + foreach (var directory in Directory.EnumerateDirectories(rootEncoderPath, "*", SearchOption.TopDirectoryOnly) + .ToList()) + { + var allFiles = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).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; + } + + private async void DownloadFFMpegInBackground(FFMpegInstallInfo downloadinfo, string directory) + { + try + { + await DownloadFFMpeg(downloadinfo, directory, new Progress()).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading ffmpeg", ex); + } + } + + private async Task DownloadFFMpeg(FFMpegInstallInfo downloadinfo, string directory, IProgress progress) + { + if (downloadinfo.IsEmbedded) + { + var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); + _fileSystem.CreateDirectory(Path.GetDirectoryName(tempFile)); + + using (var stream = _ownerAssembly.GetManifestResourceStream(downloadinfo.DownloadUrls[0])) + { + using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + { + await stream.CopyToAsync(fs).ConfigureAwait(false); + } + } + ExtractFFMpeg(downloadinfo, tempFile, directory); + } + + foreach (var url in downloadinfo.DownloadUrls) + { + progress.Report(0); + + try + { + var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions + { + Url = url, + CancellationToken = CancellationToken.None, + Progress = progress + + }).ConfigureAwait(false); + + ExtractFFMpeg(downloadinfo, tempFile, directory); + return; + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading {0}", ex, url); + } + } + + if (downloadinfo.DownloadUrls.Length == 0) + { + throw new ApplicationException("ffmpeg unvailable. Please install it and start the server with two command line arguments: -ffmpeg \"{PATH}\" and -ffprobe \"{PATH}\""); + } + + throw new ApplicationException("Unable to download required components. Please try again later."); + } + + private void ExtractFFMpeg(FFMpegInstallInfo downloadinfo, string tempFile, string targetFolder) + { + _logger.Info("Extracting ffmpeg from {0}", tempFile); + + var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); + + _fileSystem.CreateDirectory(tempFolder); + + try + { + ExtractArchive(downloadinfo, tempFile, tempFolder); + + var files = Directory.EnumerateFiles(tempFolder, "*", SearchOption.AllDirectories) + .ToList(); + + foreach (var file in files.Where(i => + { + var filename = Path.GetFileName(i); + + return + string.Equals(filename, downloadinfo.FFProbeFilename, StringComparison.OrdinalIgnoreCase) || + string.Equals(filename, downloadinfo.FFMpegFilename, StringComparison.OrdinalIgnoreCase); + })) + { + var targetFile = Path.Combine(targetFolder, Path.GetFileName(file)); + _fileSystem.CopyFile(file, targetFile, true); + SetFilePermissions(targetFile); + } + } + finally + { + DeleteFile(tempFile); + } + } + + private void SetFilePermissions(string path) + { + // Linux: File permission to 666, and user's execute bit + if (_environment.OperatingSystem == OperatingSystem.Bsd || _environment.OperatingSystem == OperatingSystem.Linux || _environment.OperatingSystem == OperatingSystem.Osx) + { + _logger.Info("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path); + + Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH); + } + } + + private void ExtractArchive(FFMpegInstallInfo downloadinfo, string archivePath, string targetPath) + { + _logger.Info("Extracting {0} to {1}", archivePath, targetPath); + + if (string.Equals(downloadinfo.ArchiveType, "7z", StringComparison.OrdinalIgnoreCase)) + { + _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); + } + else if (string.Equals(downloadinfo.ArchiveType, "gz", StringComparison.OrdinalIgnoreCase)) + { + _zipClient.ExtractAllFromTar(archivePath, targetPath, true); + } + } + private void Extract7zArchive(string archivePath, string targetPath) + { + _logger.Info("Extracting {0} to {1}", archivePath, targetPath); + + _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); + } + + private void DeleteFile(string path) + { + try + { + _fileSystem.DeleteFile(path); + } + catch (IOException ex) + { + _logger.ErrorException("Error deleting temp file {0}", ex, path); + } + } + + /// + /// Extracts the fonts. + /// + /// The target path. + /// Task. + private 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 + Task.Run(async () => + { + await DownloadFontFile(fontsDirectory, fontFilename, new Progress()).ConfigureAwait(false); + + await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); + }); + } + } + catch (HttpException ex) + { + // Don't let the server crash because of this + _logger.ErrorException("Error downloading ffmpeg font files", ex); + } + catch (Exception ex) + { + // Don't let the server crash because of this + _logger.ErrorException("Error writing ffmpeg font files", ex); + } + } + + /// + /// Downloads the font file. + /// + /// The fonts directory. + /// The font filename. + /// Task. + private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress progress) + { + var existingFile = Directory + .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories) + .FirstOrDefault(); + + 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.ErrorException("Error copying file", ex); + } + } + + 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.ErrorException("Failed to download ffmpeg font file from {0}", ex, 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.ErrorException("Error deleting temp file {0}", ex, tempFile); + } + } + + /// + /// Writes the font config file. + /// + /// The fonts directory. + /// Task. + 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("{0}ArialArial Unicode MS", fontsDirectory); + + var bytes = Encoding.UTF8.GetBytes(contents); + + using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileMode.Create, FileAccess.Write, + FileShare.Read, true)) + { + await fileStream.WriteAsync(bytes, 0, bytes.Length); + } + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/INativeApp.cs b/MediaBrowser.Server.Startup.Common/INativeApp.cs index 9df670bda..121d4192e 100644 --- a/MediaBrowser.Server.Startup.Common/INativeApp.cs +++ b/MediaBrowser.Server.Startup.Common/INativeApp.cs @@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging; using System.Collections.Generic; using System.Reflection; using MediaBrowser.Controller.Power; +using MediaBrowser.Server.Startup.Common.FFMpeg; namespace MediaBrowser.Server.Startup.Common { @@ -97,5 +98,7 @@ namespace MediaBrowser.Server.Startup.Common /// /// IPowerManagement. IPowerManagement GetPowerManagement(); + + FFMpegInstallInfo GetFfmpegInstallInfo(); } } diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index 80ce88fa3..19ce9ed9e 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -65,8 +65,8 @@ - - + + diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index 164037dc5..056258f96 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Reflection; using CommonIO; using MediaBrowser.Controller.Power; +using MediaBrowser.Server.Startup.Common.FFMpeg; namespace MediaBrowser.ServerApplication.Native { @@ -30,7 +31,7 @@ namespace MediaBrowser.ServerApplication.Native } list.Add(GetType().Assembly); - + return list; } @@ -124,5 +125,32 @@ namespace MediaBrowser.ServerApplication.Native { return new WindowsPowerManagement(_logger); } + + public FFMpegInstallInfo GetFfmpegInstallInfo() + { + var info = new FFMpegInstallInfo(); + + info.FFMpegFilename = "ffmpeg.exe"; + info.FFProbeFilename = "ffprobe.exe"; + info.Version = "20160401"; + info.ArchiveType = "7z"; + info.IsEmbedded = true; + info.DownloadUrls = GetDownloadUrls(); + + return info; + } + + private string[] GetDownloadUrls() + { + switch (Environment.SystemArchitecture) + { + case Architecture.X86_X64: + return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx64.7z" }; + case Architecture.X86: + return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx86.7z" }; + } + + return new string[] { }; + } } } -- cgit v1.2.3 From 91ee2e22a82773a86832a0ce96a309403ab3bb78 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 3 Apr 2016 13:34:52 -0400 Subject: update components --- .../MediaBrowser.ServerApplication.csproj | 1 - MediaBrowser.ServerApplication/Native/Autorun.cs | 35 ---------------------- .../Native/WindowsApp.cs | 18 ++++++++++- 3 files changed, 17 insertions(+), 37 deletions(-) delete mode 100644 MediaBrowser.ServerApplication/Native/Autorun.cs (limited to 'MediaBrowser.ServerApplication/Native/WindowsApp.cs') diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index a654bd2b5..f1a2915fe 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -110,7 +110,6 @@ MainForm.cs - diff --git a/MediaBrowser.ServerApplication/Native/Autorun.cs b/MediaBrowser.ServerApplication/Native/Autorun.cs deleted file mode 100644 index 4d3f66b91..000000000 --- a/MediaBrowser.ServerApplication/Native/Autorun.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.IO; -using CommonIO; - -namespace MediaBrowser.ServerApplication.Native -{ - /// - /// Class Autorun - /// - public static class Autorun - { - /// - /// Configures the specified autorun. - /// - /// if set to true [autorun]. - /// The file system. - public static void Configure(bool autorun, IFileSystem fileSystem) - { - var shortcutPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Emby", "Emby Server.lnk"); - - var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup); - - if (autorun) - { - //Copy our shortut into the startup folder for this user - File.Copy(shortcutPath, Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk"), true); - } - else - { - //Remove our shortcut from the startup folder for this user - fileSystem.DeleteFile(Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk")); - } - } - } -} diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index 056258f96..146a4372b 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Server.Startup.Common; using MediaBrowser.ServerApplication.Networking; using System.Collections.Generic; +using System.IO; using System.Reflection; using CommonIO; using MediaBrowser.Controller.Power; @@ -108,7 +109,22 @@ namespace MediaBrowser.ServerApplication.Native public void ConfigureAutoRun(bool autorun) { - Autorun.Configure(autorun, _fileSystem); + var shortcutPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.StartMenu), "Emby", "Emby Server.lnk"); + + var startupPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Startup); + + if (autorun) + { + //Copy our shortut into the startup folder for this user + var targetPath = Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk"); + _fileSystem.CreateDirectory(Path.GetDirectoryName(targetPath)); + File.Copy(shortcutPath, targetPath, true); + } + else + { + //Remove our shortcut from the startup folder for this user + _fileSystem.DeleteFile(Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk")); + } } public INetworkManager CreateNetworkManager(ILogger logger) -- cgit v1.2.3