< Summary - Jellyfin

Information
Class: Jellyfin.Server.ServerSetupApp.StartupUiRenderer
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 65
Coverable lines: 65
Total lines: 109
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 6/28/2026 - 12:15:35 AM Line coverage: 0% (0/65) Total lines: 109

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
CreateAsync()100%210%
RenderAsync(...)100%210%

File(s)

/srv/git/jellyfin/Jellyfin.Server/ServerSetupApp/StartupUiRenderer.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Linq;
 5using System.Threading.Tasks;
 6using MediaBrowser.Model.IO;
 7using Microsoft.Extensions.Logging;
 8using Morestachio;
 9using Morestachio.Framework.IO.SingleStream;
 10using Morestachio.Rendering;
 11
 12namespace Jellyfin.Server.ServerSetupApp;
 13
 14/// <summary>
 15/// Compiles and renders the startup UI Morestachio template.
 16/// Shared by the live <see cref="SetupServer"/> and the standalone startup UI preview tool so both
 17/// exercise the exact same template and formatters.
 18/// </summary>
 19public sealed class StartupUiRenderer
 20{
 21    private readonly IRenderer _renderer;
 22
 23    private StartupUiRenderer(IRenderer renderer)
 24    {
 025        _renderer = renderer;
 026    }
 27
 28    /// <summary>
 29    /// Compiles the startup UI template located at <paramref name="templatePath"/>.
 30    /// </summary>
 31    /// <param name="templatePath">The full path to the <c>index.mstemplate.html</c> template.</param>
 32    /// <returns>A ready to use <see cref="StartupUiRenderer"/>.</returns>
 33    public static async Task<StartupUiRenderer> CreateAsync(string templatePath)
 34    {
 035        var fileTemplate = await File.ReadAllTextAsync(templatePath).ConfigureAwait(false);
 036        var renderer = (await ParserOptionsBuilder.New()
 037            .WithTemplate(fileTemplate)
 038            .WithFormatter(
 039            (Version version, int arg) =>
 040            {
 041                // version type does not for some stupid reason implement IFormattable which morestachio relies on for T
 042                return version.ToString(arg);
 043            },
 044            "ToString")
 045            .WithFormatter(
 046                (StartupLogTopic logEntry, IEnumerable<StartupLogTopic> children) =>
 047                {
 048                    if (children.Any())
 049                    {
 050                        var maxLevel = logEntry.LogLevel;
 051                        var stack = new Stack<StartupLogTopic>(children);
 052
 053                        while (maxLevel != LogLevel.Error && stack.Count > 0 && (logEntry = stack.Pop()) is not null) //
 054                        {
 055                            maxLevel = maxLevel < logEntry.LogLevel ? logEntry.LogLevel : maxLevel;
 056                            foreach (var child in logEntry.Children)
 057                            {
 058                                stack.Push(child);
 059                            }
 060                        }
 061
 062                        return maxLevel;
 063                    }
 064
 065                    return logEntry.LogLevel;
 066                },
 067                "FormatLogLevel")
 068            .WithFormatter(
 069                (LogLevel logLevel) =>
 070                {
 071                    switch (logLevel)
 072                    {
 073                        case LogLevel.Trace:
 074                        case LogLevel.Debug:
 075                        case LogLevel.None:
 076                            return "success";
 077                        case LogLevel.Information:
 078                            return "info";
 079                        case LogLevel.Warning:
 080                            return "warn";
 081                        case LogLevel.Error:
 082                            return "danger";
 083                        case LogLevel.Critical:
 084                            return "danger-strong";
 085                    }
 086
 087                    return string.Empty;
 088                },
 089                "ToString")
 090            .BuildAndParseAsync()
 091            .ConfigureAwait(false))
 092            .CreateCompiledRenderer();
 93
 094        return new StartupUiRenderer(renderer);
 095    }
 96
 97    /// <summary>
 98    /// Renders the template with the provided model into the target stream.
 99    /// </summary>
 100    /// <param name="model">The values made available to the template.</param>
 101    /// <param name="output">The stream the rendered HTML is written to.</param>
 102    /// <returns>A Task.</returns>
 103    public Task RenderAsync(IDictionary<string, object> model, Stream output)
 104    {
 0105        return _renderer.RenderAsync(
 0106            model,
 0107            new ByteCounterStream(output, IODefaults.FileStreamBufferSize, true, _renderer.ParserOptions));
 108    }
 109}