Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 13s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 16s
PR Gates / Docs update reminder (pull_request) Successful in 17s
PR Gates / decisions lifecycle (pull_request) Successful in 21s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 1m28s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 15m46s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 15m50s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 20m18s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
The #74 seeded on-now-next.yml never rendered at transcode time: - format_datetime was called with 2 args; it needs 3 (DateTimeOffset, timeZoneId, format) and does the tz conversion itself, so the Scriban render threw. Drop the NEXT start-time (avoids hardcoding a timezone in a shipped default). - styles had no font_family; CustomFontMapper.TypefaceFromStyle crashes on a null family before its default-font fallback. Add font_family: Noto Sans. Verified live on ersatztv-test via frame capture. Adds a seeder test that deserializes the YAML and asserts base_style resolves + every style sets a font. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
83 lines
2.7 KiB
C#
83 lines
2.7 KiB
C#
using System.IO.Abstractions;
|
|
using ErsatzTV.Core;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Graphics;
|
|
using ErsatzTV.Infrastructure.Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace ErsatzTV.Infrastructure.Streaming.Graphics;
|
|
|
|
public static class GraphicsElementSeeder
|
|
{
|
|
private const string OnNowNextYaml =
|
|
"""
|
|
name: On Now / Next
|
|
epg_entries: 2
|
|
location: BottomLeft
|
|
horizontal_margin_percent: 4
|
|
vertical_margin_percent: 8
|
|
width_percent: 42
|
|
text_fit: Wrap
|
|
text_align: Left
|
|
z_index: 100
|
|
# transparent until 4s in, fade in 1s, hold 6s, fade out 1s
|
|
opacity_expression: "LinearFadeDuration(content_seconds, 4, 1, 6)"
|
|
base_style: now
|
|
styles:
|
|
- name: now
|
|
font_family: "Noto Sans"
|
|
font_size: 30
|
|
font_weight: 700
|
|
text_color: "#FFFFFF"
|
|
halo_color: "#000000"
|
|
halo_width: 2
|
|
- name: sub
|
|
font_family: "Noto Sans"
|
|
font_size: 22
|
|
font_weight: 400
|
|
text_color: "#DDDDDD"
|
|
halo_color: "#000000"
|
|
halo_width: 2
|
|
- name: next
|
|
font_family: "Noto Sans"
|
|
font_size: 22
|
|
font_weight: 400
|
|
text_color: "#DDDDDD"
|
|
halo_color: "#000000"
|
|
halo_width: 2
|
|
text: |
|
|
[now]NOW {{ Epg[0].Title }}[/now]
|
|
{{ if Epg[0].SubTitle }}[sub]{{ Epg[0].SubTitle }}[/sub]{{ end }}
|
|
{{ if (array.size Epg) > 1 }}[next]NEXT {{ Epg[1].Title }}[/next]{{ end }}
|
|
""";
|
|
|
|
public static async Task SeedOnNowNext(TvContext context, IFileSystem fileSystem, CancellationToken cancellationToken)
|
|
{
|
|
string seededKey = ConfigElementKey.GraphicsOnNowNextSeeded.Key;
|
|
bool alreadySeeded = await context.ConfigElements.AnyAsync(c => c.Key == seededKey, cancellationToken);
|
|
if (alreadySeeded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string folder = FileSystemLayout.GraphicsElementsTextTemplatesFolder;
|
|
string target = fileSystem.Path.Combine(folder, GraphicsElementDefaults.OnNowNextFileName);
|
|
|
|
if (!fileSystem.Directory.Exists(folder))
|
|
{
|
|
fileSystem.Directory.CreateDirectory(folder);
|
|
}
|
|
|
|
// Adopt an operator's existing file untouched; only write when absent.
|
|
if (!fileSystem.File.Exists(target))
|
|
{
|
|
await fileSystem.File.WriteAllTextAsync(target, OnNowNextYaml, cancellationToken);
|
|
}
|
|
|
|
await context.ConfigElements.AddAsync(
|
|
new ConfigElement { Key = seededKey, Value = "true" },
|
|
cancellationToken);
|
|
await context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
}
|