diff options
| -rw-r--r-- | MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj | 2 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs | 232 | ||||
| -rw-r--r-- | MediaBrowser.ServerApplication/ApplicationHost.cs | 15 | ||||
| -rw-r--r-- | MediaBrowser.ServerApplication/Implementations/FFMpegDownloader.cs | 205 | ||||
| -rw-r--r-- | MediaBrowser.ServerApplication/Implementations/ffmpeg20130904.zip.REMOVED.git-id (renamed from MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130904.zip.REMOVED.git-id) | 0 | ||||
| -rw-r--r-- | MediaBrowser.ServerApplication/Implementations/readme.txt (renamed from MediaBrowser.Server.Implementations/MediaEncoder/readme.txt) | 0 | ||||
| -rw-r--r-- | MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj | 5 |
7 files changed, 233 insertions, 226 deletions
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 423f27c4a..9d2fc8c6b 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -254,10 +254,8 @@ <EmbeddedResource Include="Localization\Ratings\kz.txt" /> <EmbeddedResource Include="Localization\Ratings\nz.txt" /> <EmbeddedResource Include="Localization\Ratings\ru.txt" /> - <EmbeddedResource Include="MediaEncoder\readme.txt" /> </ItemGroup> <ItemGroup> - <EmbeddedResource Include="MediaEncoder\ffmpeg20130904.zip" /> <None Include="packages.config" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index 5792806d8..b24c9a5ca 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -1,9 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Common.Net; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; @@ -13,7 +11,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -27,12 +24,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder public class MediaEncoder : IMediaEncoder, IDisposable { /// <summary> - /// Gets or sets the zip client. - /// </summary> - /// <value>The zip client.</value> - private readonly IZipClient _zipClient; - - /// <summary> /// The _logger /// </summary> private readonly ILogger _logger; @@ -48,8 +39,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// <value>The json serializer.</value> private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; - /// <summary> /// The video image resource pool /// </summary> @@ -70,50 +59,25 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// </summary> private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(2, 2); - /// <summary> - /// Gets or sets the versioned directory path. - /// </summary> - /// <value>The versioned directory path.</value> - private string VersionedDirectoryPath { get; set; } + public string FFMpegPath { get; private set; } - /// <summary> - /// Initializes a new instance of the <see cref="MediaEncoder" /> class. - /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="zipClient">The zip client.</param> - /// <param name="appPaths">The app paths.</param> - /// <param name="jsonSerializer">The json serializer.</param> - public MediaEncoder(ILogger logger, IZipClient zipClient, IApplicationPaths appPaths, - IJsonSerializer jsonSerializer, IHttpClient httpClient) + public string FFProbePath { get; private set; } + + public string Version { get; private set; } + + public MediaEncoder(ILogger logger, IApplicationPaths appPaths, + IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version) { _logger = logger; - _zipClient = zipClient; _appPaths = appPaths; _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + Version = version; + FFProbePath = ffProbePath; + FFMpegPath = ffMpegPath; // Not crazy about this but it's the only way to suppress ffmpeg crash dialog boxes SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT | ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX); - - Task.Run(() => VersionedDirectoryPath = GetVersionedDirectoryPath()); - } - - /// <summary> - /// Gets the media tools path. - /// </summary> - /// <param name="create">if set to <c>true</c> [create].</param> - /// <returns>System.String.</returns> - private string GetMediaToolsPath(bool create) - { - var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); - - if (create && !Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - return path; } /// <summary> @@ -126,182 +90,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } /// <summary> - /// The _ FF MPEG path - /// </summary> - private string _FFMpegPath; - - /// <summary> - /// Gets the path to ffmpeg.exe - /// </summary> - /// <value>The FF MPEG path.</value> - public string FFMpegPath - { - get { return _FFMpegPath ?? (_FFMpegPath = Path.Combine(VersionedDirectoryPath, "ffmpeg.exe")); } - } - - /// <summary> - /// The _ FF probe path - /// </summary> - private string _FFProbePath; - - /// <summary> - /// Gets the path to ffprobe.exe - /// </summary> - /// <value>The FF probe path.</value> - private string FFProbePath - { - get { return _FFProbePath ?? (_FFProbePath = Path.Combine(VersionedDirectoryPath, "ffprobe.exe")); } - } - - /// <summary> - /// Gets the version. - /// </summary> - /// <value>The version.</value> - public string Version - { - get { return Path.GetFileNameWithoutExtension(VersionedDirectoryPath); } - } - - /// <summary> - /// Gets the versioned directory path. - /// </summary> - /// <returns>System.String.</returns> - private string GetVersionedDirectoryPath() - { - var assembly = GetType().Assembly; - - var prefix = GetType().Namespace + "."; - - var srch = prefix + "ffmpeg"; - - var resource = assembly.GetManifestResourceNames().First(r => r.StartsWith(srch)); - - var filename = - resource.Substring(resource.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) + prefix.Length); - - var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true), - Path.GetFileNameWithoutExtension(filename)); - - if (!Directory.Exists(versionedDirectoryPath)) - { - Directory.CreateDirectory(versionedDirectoryPath); - } - - ExtractTools(assembly, resource, versionedDirectoryPath); - - return versionedDirectoryPath; - } - - /// <summary> - /// Extracts the tools. - /// </summary> - /// <param name="assembly">The assembly.</param> - /// <param name="zipFileResourcePath">The zip file resource path.</param> - /// <param name="targetPath">The target path.</param> - private async void ExtractTools(Assembly assembly, string zipFileResourcePath, string targetPath) - { - using (var resourceStream = assembly.GetManifestResourceStream(zipFileResourcePath)) - { - _zipClient.ExtractAll(resourceStream, targetPath, false); - } - - try - { - await DownloadFonts(targetPath).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting ffmpeg font files", ex); - } - } - - private const string FontUrl = "https://www.dropbox.com/s/9nb76tybcsw5xrk/ARIALUNI.zip?dl=1"; - - /// <summary> - /// Extracts the fonts. - /// </summary> - /// <param name="targetPath">The target path.</param> - private async Task DownloadFonts(string targetPath) - { - var fontsDirectory = Path.Combine(targetPath, "fonts"); - - if (!Directory.Exists(fontsDirectory)) - { - Directory.CreateDirectory(fontsDirectory); - } - - const string fontFilename = "ARIALUNI.TTF"; - - var fontFile = Path.Combine(fontsDirectory, fontFilename); - - if (!File.Exists(fontFile)) - { - await DownloadFontFile(fontsDirectory, fontFilename).ConfigureAwait(false); - } - - await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); - } - - private async Task DownloadFontFile(string fontsDirectory, string fontFilename) - { - var existingFile = Directory - .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories) - .FirstOrDefault(); - - if (existingFile != null) - { - try - { - File.Copy(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); - } - } - - var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions - { - Url = FontUrl, - Progress = new Progress<double>() - }); - - _zipClient.ExtractAll(tempFile, fontsDirectory, true); - - try - { - File.Delete(tempFile); - } - catch (IOException ex) - { - // Log this, but don't let it fail the operation - _logger.ErrorException("Error deleting temp file {0}", ex, tempFile); - } - } - - private async Task WriteFontConfigFile(string fontsDirectory) - { - const string fontConfigFilename = "fonts.conf"; - var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename); - - if (!File.Exists(fontConfigFile)) - { - var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory); - - var bytes = Encoding.UTF8.GetBytes(contents); - - using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write, - FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, - FileOptions.Asynchronous)) - { - await fileStream.WriteAsync(bytes, 0, bytes.Length); - } - } - } - - /// <summary> /// Gets the media info. /// </summary> /// <param name="inputFiles">The input files.</param> diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 2d19260dc..5cae99785 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -284,8 +284,7 @@ namespace MediaBrowser.ServerApplication RegisterSingleInstance<ILibrarySearchEngine>(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager)); - MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ZipClient, ApplicationPaths, JsonSerializer, HttpClient); - RegisterSingleInstance(MediaEncoder); + await RegisterMediaEncoder().ConfigureAwait(false); var clientConnectionManager = new SessionManager(UserDataRepository, ServerConfigurationManager, Logger, UserRepository); RegisterSingleInstance<ISessionManager>(clientConnectionManager); @@ -317,6 +316,18 @@ namespace MediaBrowser.ServerApplication } /// <summary> + /// Registers the media encoder. + /// </summary> + /// <returns>Task.</returns> + private async Task RegisterMediaEncoder() + { + var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient).GetFFMpegInfo().ConfigureAwait(false); + + MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ApplicationPaths, JsonSerializer, info.Path, info.ProbePath, info.Version); + RegisterSingleInstance(MediaEncoder); + } + + /// <summary> /// Sets the kernel properties. /// </summary> private void SetKernelProperties() diff --git a/MediaBrowser.ServerApplication/Implementations/FFMpegDownloader.cs b/MediaBrowser.ServerApplication/Implementations/FFMpegDownloader.cs new file mode 100644 index 000000000..7fd0acddd --- /dev/null +++ b/MediaBrowser.ServerApplication/Implementations/FFMpegDownloader.cs @@ -0,0 +1,205 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.ServerApplication.Implementations +{ + public class FFMpegDownloader + { + private readonly IZipClient _zipClient; + private readonly IHttpClient _httpClient; + private readonly IApplicationPaths _appPaths; + private readonly ILogger _logger; + + public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient) + { + _logger = logger; + _appPaths = appPaths; + _httpClient = httpClient; + _zipClient = zipClient; + } + + public async Task<FFMpegInfo> GetFFMpegInfo() + { + var assembly = GetType().Assembly; + + var prefix = GetType().Namespace + "."; + + var srch = prefix + "ffmpeg"; + + var resource = assembly.GetManifestResourceNames().First(r => r.StartsWith(srch)); + + var filename = + resource.Substring(resource.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) + prefix.Length); + + var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true), + Path.GetFileNameWithoutExtension(filename)); + + if (!Directory.Exists(versionedDirectoryPath)) + { + Directory.CreateDirectory(versionedDirectoryPath); + } + + await ExtractTools(assembly, resource, versionedDirectoryPath).ConfigureAwait(false); + + return new FFMpegInfo + { + ProbePath = Path.Combine(versionedDirectoryPath, "ffprobe.exe"), + Path = Path.Combine(versionedDirectoryPath, "ffmpeg.exe"), + Version = Path.GetFileNameWithoutExtension(versionedDirectoryPath) + }; + } + + /// <summary> + /// Extracts the tools. + /// </summary> + /// <param name="assembly">The assembly.</param> + /// <param name="zipFileResourcePath">The zip file resource path.</param> + /// <param name="targetPath">The target path.</param> + private async Task ExtractTools(Assembly assembly, string zipFileResourcePath, string targetPath) + { + using (var resourceStream = assembly.GetManifestResourceStream(zipFileResourcePath)) + { + _zipClient.ExtractAll(resourceStream, targetPath, false); + } + + try + { + await DownloadFonts(targetPath).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting ffmpeg font files", ex); + } + } + + private const string FontUrl = "https://www.dropbox.com/s/9nb76tybcsw5xrk/ARIALUNI.zip?dl=1"; + + /// <summary> + /// Extracts the fonts. + /// </summary> + /// <param name="targetPath">The target path.</param> + private async Task DownloadFonts(string targetPath) + { + var fontsDirectory = Path.Combine(targetPath, "fonts"); + + if (!Directory.Exists(fontsDirectory)) + { + Directory.CreateDirectory(fontsDirectory); + } + + const string fontFilename = "ARIALUNI.TTF"; + + var fontFile = Path.Combine(fontsDirectory, fontFilename); + + if (!File.Exists(fontFile)) + { + await DownloadFontFile(fontsDirectory, fontFilename).ConfigureAwait(false); + } + + await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); + } + + /// <summary> + /// Downloads the font file. + /// </summary> + /// <param name="fontsDirectory">The fonts directory.</param> + /// <param name="fontFilename">The font filename.</param> + /// <returns>Task.</returns> + private async Task DownloadFontFile(string fontsDirectory, string fontFilename) + { + var existingFile = Directory + .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories) + .FirstOrDefault(); + + if (existingFile != null) + { + try + { + File.Copy(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); + } + } + + var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions + { + Url = FontUrl, + Progress = new Progress<double>() + }); + + _zipClient.ExtractAll(tempFile, fontsDirectory, true); + + try + { + File.Delete(tempFile); + } + catch (IOException ex) + { + // Log this, but don't let it fail the operation + _logger.ErrorException("Error deleting temp file {0}", ex, tempFile); + } + } + + /// <summary> + /// Writes the font config file. + /// </summary> + /// <param name="fontsDirectory">The fonts directory.</param> + /// <returns>Task.</returns> + private async Task WriteFontConfigFile(string fontsDirectory) + { + const string fontConfigFilename = "fonts.conf"; + var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename); + + if (!File.Exists(fontConfigFile)) + { + var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory); + + var bytes = Encoding.UTF8.GetBytes(contents); + + using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write, + FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, + FileOptions.Asynchronous)) + { + await fileStream.WriteAsync(bytes, 0, bytes.Length); + } + } + } + + /// <summary> + /// Gets the media tools path. + /// </summary> + /// <param name="create">if set to <c>true</c> [create].</param> + /// <returns>System.String.</returns> + private string GetMediaToolsPath(bool create) + { + var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); + + if (create && !Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + return path; + } + } + + public class FFMpegInfo + { + public string Path { get; set; } + public string ProbePath { get; set; } + public string Version { get; set; } + } +} diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130904.zip.REMOVED.git-id b/MediaBrowser.ServerApplication/Implementations/ffmpeg20130904.zip.REMOVED.git-id index e99d115a4..e99d115a4 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/ffmpeg20130904.zip.REMOVED.git-id +++ b/MediaBrowser.ServerApplication/Implementations/ffmpeg20130904.zip.REMOVED.git-id diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/readme.txt b/MediaBrowser.ServerApplication/Implementations/readme.txt index b32dd9aec..b32dd9aec 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/readme.txt +++ b/MediaBrowser.ServerApplication/Implementations/readme.txt diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index f19ffe5b0..043d5c18f 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -210,6 +210,7 @@ </Compile> <Compile Include="EntryPoints\StartupWizard.cs" /> <Compile Include="EntryPoints\UdpServerEntryPoint.cs" /> + <Compile Include="Implementations\FFMpegDownloader.cs" /> <Compile Include="MainStartup.cs" /> <Compile Include="BackgroundServiceInstaller.cs"> <SubType>Component</SubType> @@ -277,6 +278,7 @@ <LastGenOutput>Resources.Designer.cs</LastGenOutput> </EmbeddedResource> <None Include="app.manifest" /> + <EmbeddedResource Include="Implementations\ffmpeg20130904.zip" /> <None Include="packages.config" /> <None Include="Properties\Settings.settings"> <Generator>SettingsSingleFileGenerator</Generator> @@ -388,6 +390,9 @@ <ItemGroup> <Resource Include="Resources\Images\mb3logo800.png" /> </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Implementations\readme.txt" /> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <PropertyGroup> <PostBuildEvent>if $(ConfigurationName) == Release ( |
