2
0

FontConfigLoader.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using MediaBrowser.Model.IO;
  7. using MediaBrowser.Common.Configuration;
  8. using MediaBrowser.Common.Net;
  9. using MediaBrowser.Controller.IO;
  10. using MediaBrowser.Model.IO;
  11. using MediaBrowser.Model.Logging;
  12. using MediaBrowser.Model.Net;
  13. namespace MediaBrowser.MediaEncoding.Encoder
  14. {
  15. public class FontConfigLoader
  16. {
  17. private readonly IHttpClient _httpClient;
  18. private readonly IApplicationPaths _appPaths;
  19. private readonly ILogger _logger;
  20. private readonly IZipClient _zipClient;
  21. private readonly IFileSystem _fileSystem;
  22. private readonly string[] _fontUrls =
  23. {
  24. "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z"
  25. };
  26. public FontConfigLoader(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, IZipClient zipClient, IFileSystem fileSystem)
  27. {
  28. _httpClient = httpClient;
  29. _appPaths = appPaths;
  30. _logger = logger;
  31. _zipClient = zipClient;
  32. _fileSystem = fileSystem;
  33. }
  34. /// <summary>
  35. /// Extracts the fonts.
  36. /// </summary>
  37. /// <param name="targetPath">The target path.</param>
  38. /// <returns>Task.</returns>
  39. public async Task DownloadFonts(string targetPath)
  40. {
  41. try
  42. {
  43. var fontsDirectory = Path.Combine(targetPath, "fonts");
  44. _fileSystem.CreateDirectory(fontsDirectory);
  45. const string fontFilename = "ARIALUNI.TTF";
  46. var fontFile = Path.Combine(fontsDirectory, fontFilename);
  47. if (_fileSystem.FileExists(fontFile))
  48. {
  49. await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
  50. }
  51. else
  52. {
  53. // Kick this off, but no need to wait on it
  54. var task = Task.Run(async () =>
  55. {
  56. await DownloadFontFile(fontsDirectory, fontFilename, new Progress<double>()).ConfigureAwait(false);
  57. await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
  58. });
  59. }
  60. }
  61. catch (HttpException ex)
  62. {
  63. // Don't let the server crash because of this
  64. _logger.ErrorException("Error downloading ffmpeg font files", ex);
  65. }
  66. catch (Exception ex)
  67. {
  68. // Don't let the server crash because of this
  69. _logger.ErrorException("Error writing ffmpeg font files", ex);
  70. }
  71. }
  72. /// <summary>
  73. /// Downloads the font file.
  74. /// </summary>
  75. /// <param name="fontsDirectory">The fonts directory.</param>
  76. /// <param name="fontFilename">The font filename.</param>
  77. /// <returns>Task.</returns>
  78. private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress)
  79. {
  80. var existingFile = _fileSystem
  81. .GetFilePaths(_appPaths.ProgramDataPath, true)
  82. .FirstOrDefault(i => string.Equals(fontFilename, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase));
  83. if (existingFile != null)
  84. {
  85. try
  86. {
  87. _fileSystem.CopyFile(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
  88. return;
  89. }
  90. catch (IOException ex)
  91. {
  92. // Log this, but don't let it fail the operation
  93. _logger.ErrorException("Error copying file", ex);
  94. }
  95. }
  96. string tempFile = null;
  97. foreach (var url in _fontUrls)
  98. {
  99. progress.Report(0);
  100. try
  101. {
  102. tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
  103. {
  104. Url = url,
  105. Progress = progress
  106. }).ConfigureAwait(false);
  107. break;
  108. }
  109. catch (Exception ex)
  110. {
  111. // The core can function without the font file, so handle this
  112. _logger.ErrorException("Failed to download ffmpeg font file from {0}", ex, url);
  113. }
  114. }
  115. if (string.IsNullOrEmpty(tempFile))
  116. {
  117. return;
  118. }
  119. Extract7zArchive(tempFile, fontsDirectory);
  120. try
  121. {
  122. _fileSystem.DeleteFile(tempFile);
  123. }
  124. catch (IOException ex)
  125. {
  126. // Log this, but don't let it fail the operation
  127. _logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
  128. }
  129. }
  130. private void Extract7zArchive(string archivePath, string targetPath)
  131. {
  132. _logger.Info("Extracting {0} to {1}", archivePath, targetPath);
  133. _zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
  134. }
  135. /// <summary>
  136. /// Writes the font config file.
  137. /// </summary>
  138. /// <param name="fontsDirectory">The fonts directory.</param>
  139. /// <returns>Task.</returns>
  140. private async Task WriteFontConfigFile(string fontsDirectory)
  141. {
  142. const string fontConfigFilename = "fonts.conf";
  143. var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
  144. if (!_fileSystem.FileExists(fontConfigFile))
  145. {
  146. var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
  147. var bytes = Encoding.UTF8.GetBytes(contents);
  148. using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileOpenMode.Create, FileAccessMode.Write,
  149. FileShareMode.Read, true))
  150. {
  151. await fileStream.WriteAsync(bytes, 0, bytes.Length);
  152. }
  153. }
  154. }
  155. }
  156. }