aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs')
-rw-r--r--Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs109
1 files changed, 109 insertions, 0 deletions
diff --git a/Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs b/Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs
new file mode 100644
index 0000000000..db07b9d8c1
--- /dev/null
+++ b/Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+using Microsoft.Extensions.Logging;
+using Morestachio;
+using Morestachio.Framework.IO.SingleStream;
+using Morestachio.Rendering;
+
+namespace Jellyfin.Server.ServerSetupApp;
+
+/// <summary>
+/// Compiles and renders the startup UI Morestachio template.
+/// Shared by the live <see cref="SetupServer"/> and the standalone startup UI preview tool so both
+/// exercise the exact same template and formatters.
+/// </summary>
+public sealed class StartupUiRenderer
+{
+ private readonly IRenderer _renderer;
+
+ private StartupUiRenderer(IRenderer renderer)
+ {
+ _renderer = renderer;
+ }
+
+ /// <summary>
+ /// Compiles the startup UI template located at <paramref name="templatePath"/>.
+ /// </summary>
+ /// <param name="templatePath">The full path to the <c>index.mstemplate.html</c> template.</param>
+ /// <returns>A ready to use <see cref="StartupUiRenderer"/>.</returns>
+ public static async Task<StartupUiRenderer> CreateAsync(string templatePath)
+ {
+ var fileTemplate = await File.ReadAllTextAsync(templatePath).ConfigureAwait(false);
+ var renderer = (await ParserOptionsBuilder.New()
+ .WithTemplate(fileTemplate)
+ .WithFormatter(
+ (Version version, int arg) =>
+ {
+ // version type does not for some stupid reason implement IFormattable which morestachio relies on for ToString support therefor we need to do it manually.
+ return version.ToString(arg);
+ },
+ "ToString")
+ .WithFormatter(
+ (StartupLogTopic logEntry, IEnumerable<StartupLogTopic> children) =>
+ {
+ if (children.Any())
+ {
+ var maxLevel = logEntry.LogLevel;
+ var stack = new Stack<StartupLogTopic>(children);
+
+ while (maxLevel != LogLevel.Error && stack.Count > 0 && (logEntry = stack.Pop()) is not null) // error is the highest inherted error level.
+ {
+ maxLevel = maxLevel < logEntry.LogLevel ? logEntry.LogLevel : maxLevel;
+ foreach (var child in logEntry.Children)
+ {
+ stack.Push(child);
+ }
+ }
+
+ return maxLevel;
+ }
+
+ return logEntry.LogLevel;
+ },
+ "FormatLogLevel")
+ .WithFormatter(
+ (LogLevel logLevel) =>
+ {
+ switch (logLevel)
+ {
+ case LogLevel.Trace:
+ case LogLevel.Debug:
+ case LogLevel.None:
+ return "success";
+ case LogLevel.Information:
+ return "info";
+ case LogLevel.Warning:
+ return "warn";
+ case LogLevel.Error:
+ return "danger";
+ case LogLevel.Critical:
+ return "danger-strong";
+ }
+
+ return string.Empty;
+ },
+ "ToString")
+ .BuildAndParseAsync()
+ .ConfigureAwait(false))
+ .CreateCompiledRenderer();
+
+ return new StartupUiRenderer(renderer);
+ }
+
+ /// <summary>
+ /// Renders the template with the provided model into the target stream.
+ /// </summary>
+ /// <param name="model">The values made available to the template.</param>
+ /// <param name="output">The stream the rendered HTML is written to.</param>
+ /// <returns>A Task.</returns>
+ public Task RenderAsync(IDictionary<string, object> model, Stream output)
+ {
+ return _renderer.RenderAsync(
+ model,
+ new ByteCounterStream(output, IODefaults.FileStreamBufferSize, true, _renderer.ParserOptions));
+ }
+}