Explorar el Código

Merge pull request #3683 from nyanmisaka/fonts

Allows to provide multiple fallback fonts for client to render subtitles
Anthony Lavado hace 4 años
padre
commit
7971978081

+ 97 - 0
Jellyfin.Api/Controllers/SubtitleController.cs

@@ -12,6 +12,8 @@ using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Models.SubtitleDtos;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
@@ -22,6 +24,7 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.Subtitles;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
@@ -35,6 +38,7 @@ namespace Jellyfin.Api.Controllers
     [Route("")]
     public class SubtitleController : BaseJellyfinApiController
     {
+        private readonly IServerConfigurationManager _serverConfigurationManager;
         private readonly ILibraryManager _libraryManager;
         private readonly ISubtitleManager _subtitleManager;
         private readonly ISubtitleEncoder _subtitleEncoder;
@@ -47,6 +51,7 @@ namespace Jellyfin.Api.Controllers
         /// <summary>
         /// Initializes a new instance of the <see cref="SubtitleController"/> class.
         /// </summary>
+        /// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param>
         /// <param name="libraryManager">Instance of <see cref="ILibraryManager"/> interface.</param>
         /// <param name="subtitleManager">Instance of <see cref="ISubtitleManager"/> interface.</param>
         /// <param name="subtitleEncoder">Instance of <see cref="ISubtitleEncoder"/> interface.</param>
@@ -56,6 +61,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="authContext">Instance of <see cref="IAuthorizationContext"/> interface.</param>
         /// <param name="logger">Instance of <see cref="ILogger{SubtitleController}"/> interface.</param>
         public SubtitleController(
+            IServerConfigurationManager serverConfigurationManager,
             ILibraryManager libraryManager,
             ISubtitleManager subtitleManager,
             ISubtitleEncoder subtitleEncoder,
@@ -65,6 +71,7 @@ namespace Jellyfin.Api.Controllers
             IAuthorizationContext authContext,
             ILogger<SubtitleController> logger)
         {
+            _serverConfigurationManager = serverConfigurationManager;
             _libraryManager = libraryManager;
             _subtitleManager = subtitleManager;
             _subtitleEncoder = subtitleEncoder;
@@ -379,5 +386,95 @@ namespace Jellyfin.Api.Controllers
                 copyTimestamps,
                 CancellationToken.None);
         }
+
+        /// <summary>
+        /// Gets a list of available fallback font files.
+        /// </summary>
+        /// <response code="200">Information retrieved.</response>
+        /// <returns>An array of <see cref="FontFile"/> with the available font files.</returns>
+        [HttpGet("FallbackFont/Fonts")]
+        [Authorize(Policy = Policies.DefaultAuthorization)]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        public IEnumerable<FontFile> GetFallbackFontList()
+        {
+            var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
+            var fallbackFontPath = encodingOptions.FallbackFontPath;
+
+            if (!string.IsNullOrEmpty(fallbackFontPath))
+            {
+                var files = _fileSystem.GetFiles(fallbackFontPath, new[] { ".woff", ".woff2", ".ttf", ".otf" }, false, false);
+                var fontFiles = files
+                    .Select(i => new FontFile
+                    {
+                        Name = i.Name,
+                        Size = i.Length,
+                        DateCreated = _fileSystem.GetCreationTimeUtc(i),
+                        DateModified = _fileSystem.GetLastWriteTimeUtc(i)
+                    })
+                    .OrderBy(i => i.Size)
+                    .ThenBy(i => i.Name)
+                    .ThenByDescending(i => i.DateModified)
+                    .ThenByDescending(i => i.DateCreated);
+                // max total size 20M
+                const int MaxSize = 20971520;
+                var sizeCounter = 0L;
+                foreach (var fontFile in fontFiles)
+                {
+                    sizeCounter += fontFile.Size;
+                    if (sizeCounter >= MaxSize)
+                    {
+                        _logger.LogWarning("Some fonts will not be sent due to size limitations");
+                        yield break;
+                    }
+
+                    yield return fontFile;
+                }
+            }
+            else
+            {
+                _logger.LogWarning("The path of fallback font folder has not been set");
+                encodingOptions.EnableFallbackFont = false;
+            }
+        }
+
+        /// <summary>
+        /// Gets a fallback font file.
+        /// </summary>
+        /// <param name="name">The name of the fallback font file to get.</param>
+        /// <response code="200">Fallback font file retrieved.</response>
+        /// <returns>The fallback font file.</returns>
+        [HttpGet("FallbackFont/Fonts/{name}")]
+        [Authorize(Policy = Policies.DefaultAuthorization)]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        public ActionResult GetFallbackFont([FromRoute, Required] string name)
+        {
+            var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
+            var fallbackFontPath = encodingOptions.FallbackFontPath;
+
+            if (!string.IsNullOrEmpty(fallbackFontPath))
+            {
+                var fontFile = _fileSystem.GetFiles(fallbackFontPath)
+                    .First(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
+                var fileSize = fontFile?.Length;
+
+                if (fontFile != null && fileSize != null && fileSize > 0)
+                {
+                    _logger.LogDebug("Fallback font size is {fileSize} Bytes", fileSize);
+                    return PhysicalFile(fontFile.FullName, MimeTypes.GetMimeType(fontFile.FullName));
+                }
+                else
+                {
+                    _logger.LogWarning("The selected font is null or empty");
+                }
+            }
+            else
+            {
+                _logger.LogWarning("The path of fallback font folder has not been set");
+                encodingOptions.EnableFallbackFont = false;
+            }
+
+            // returning HTTP 204 will break the SubtitlesOctopus
+            return Ok();
+        }
     }
 }

+ 5 - 0
MediaBrowser.Model/Configuration/EncodingOptions.cs

@@ -9,6 +9,10 @@ namespace MediaBrowser.Model.Configuration
 
         public string TranscodingTempPath { get; set; }
 
+        public string FallbackFontPath { get; set; }
+
+        public bool EnableFallbackFont { get; set; }
+
         public double DownMixAudioBoost { get; set; }
 
         public int MaxMuxingQueueSize { get; set; }
@@ -69,6 +73,7 @@ namespace MediaBrowser.Model.Configuration
 
         public EncodingOptions()
         {
+            EnableFallbackFont = false;
             DownMixAudioBoost = 2;
             MaxMuxingQueueSize = 2048;
             EnableThrottling = false;

+ 34 - 0
MediaBrowser.Model/Subtitles/FontFile.cs

@@ -0,0 +1,34 @@
+using System;
+
+namespace MediaBrowser.Model.Subtitles
+{
+    /// <summary>
+    /// Class FontFile.
+    /// </summary>
+    public class FontFile
+    {
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public string? Name { get; set; }
+
+        /// <summary>
+        /// Gets or sets the size.
+        /// </summary>
+        /// <value>The size.</value>
+        public long Size { get; set; }
+
+        /// <summary>
+        /// Gets or sets the date created.
+        /// </summary>
+        /// <value>The date created.</value>
+        public DateTime DateCreated { get; set; }
+
+        /// <summary>
+        /// Gets or sets the date modified.
+        /// </summary>
+        /// <value>The date modified.</value>
+        public DateTime DateModified { get; set; }
+    }
+}