Merge pull request #3683 from nyanmisaka/fonts

Allows to provide multiple fallback fonts for client to render subtitles
pull/4444/head
Anthony Lavado 4 years ago committed by GitHub
commit 7971978081
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,6 +12,8 @@ using System.Threading.Tasks;
using Jellyfin.Api.Attributes; using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Api.Models.SubtitleDtos; using Jellyfin.Api.Models.SubtitleDtos;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
@ -22,6 +24,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Subtitles;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -35,6 +38,7 @@ namespace Jellyfin.Api.Controllers
[Route("")] [Route("")]
public class SubtitleController : BaseJellyfinApiController public class SubtitleController : BaseJellyfinApiController
{ {
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ISubtitleManager _subtitleManager; private readonly ISubtitleManager _subtitleManager;
private readonly ISubtitleEncoder _subtitleEncoder; private readonly ISubtitleEncoder _subtitleEncoder;
@ -47,6 +51,7 @@ namespace Jellyfin.Api.Controllers
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SubtitleController"/> class. /// Initializes a new instance of the <see cref="SubtitleController"/> class.
/// </summary> /// </summary>
/// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="libraryManager">Instance of <see cref="ILibraryManager"/> interface.</param> /// <param name="libraryManager">Instance of <see cref="ILibraryManager"/> interface.</param>
/// <param name="subtitleManager">Instance of <see cref="ISubtitleManager"/> interface.</param> /// <param name="subtitleManager">Instance of <see cref="ISubtitleManager"/> interface.</param>
/// <param name="subtitleEncoder">Instance of <see cref="ISubtitleEncoder"/> 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="authContext">Instance of <see cref="IAuthorizationContext"/> interface.</param>
/// <param name="logger">Instance of <see cref="ILogger{SubtitleController}"/> interface.</param> /// <param name="logger">Instance of <see cref="ILogger{SubtitleController}"/> interface.</param>
public SubtitleController( public SubtitleController(
IServerConfigurationManager serverConfigurationManager,
ILibraryManager libraryManager, ILibraryManager libraryManager,
ISubtitleManager subtitleManager, ISubtitleManager subtitleManager,
ISubtitleEncoder subtitleEncoder, ISubtitleEncoder subtitleEncoder,
@ -65,6 +71,7 @@ namespace Jellyfin.Api.Controllers
IAuthorizationContext authContext, IAuthorizationContext authContext,
ILogger<SubtitleController> logger) ILogger<SubtitleController> logger)
{ {
_serverConfigurationManager = serverConfigurationManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_subtitleManager = subtitleManager; _subtitleManager = subtitleManager;
_subtitleEncoder = subtitleEncoder; _subtitleEncoder = subtitleEncoder;
@ -379,5 +386,95 @@ namespace Jellyfin.Api.Controllers
copyTimestamps, copyTimestamps,
CancellationToken.None); 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();
}
} }
} }

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

@ -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; }
}
}
Loading…
Cancel
Save