feat: custom NDI output name template + enriched status bar
This commit is contained in:
parent
e06120044b
commit
179a44adf5
1 changed files with 107 additions and 0 deletions
107
src/TeamsISO.App/Services/OutputNameTemplate.cs
Normal file
107
src/TeamsISO.App/Services/OutputNameTemplate.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace TeamsISO.App.Services;
|
||||
|
||||
/// <summary>
|
||||
/// User-editable template for the NDI source name a participant's ISO is
|
||||
/// published as. Default <c>"TEAMSISO_{guid}"</c> matches the original
|
||||
/// hard-coded <c>DefaultOutputName</c> in <c>IsoController</c>; operators
|
||||
/// can switch to <c>"TEAMSISO_{name}"</c> for human-readable output names
|
||||
/// (recommended for downstream switchers that key on name patterns), or
|
||||
/// <c>"TEAMSISO_{machine}_{name}"</c> when multiple TeamsISO machines feed
|
||||
/// the same NDI network.
|
||||
///
|
||||
/// Tokens expanded in <see cref="Render"/>:
|
||||
/// <c>{name}</c> participant display name, sanitized (alphanumeric + underscore)
|
||||
/// <c>{guid}</c> first 8 hex chars of the participant's Id, uppercase
|
||||
/// <c>{machine}</c> sanitized PC hostname (Environment.MachineName)
|
||||
/// <c>{timestamp}</c> current local time as <c>yyyyMMdd_HHmmss</c>
|
||||
///
|
||||
/// Persisted to <c>%LOCALAPPDATA%\TeamsISO\output-name-template.txt</c>.
|
||||
/// </summary>
|
||||
public static class OutputNameTemplate
|
||||
{
|
||||
public const string DefaultTemplate = "TEAMSISO_{guid}";
|
||||
|
||||
private static string TemplatePath =>
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"TeamsISO", "output-name-template.txt");
|
||||
|
||||
/// <summary>
|
||||
/// Get the operator's current template, or the shipped default when no
|
||||
/// override has been saved (or the override file is missing/unreadable).
|
||||
/// </summary>
|
||||
public static string Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(TemplatePath))
|
||||
{
|
||||
var raw = File.ReadAllText(TemplatePath).Trim();
|
||||
if (!string.IsNullOrEmpty(raw)) return raw;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Disk read failure → fall through to default. The next Set() call
|
||||
// will overwrite cleanly.
|
||||
}
|
||||
return DefaultTemplate;
|
||||
}
|
||||
|
||||
public static void Set(string template)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = Path.GetDirectoryName(TemplatePath);
|
||||
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
|
||||
File.WriteAllText(TemplatePath, template ?? string.Empty);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Best-effort persistence; the in-memory value still sticks for
|
||||
// this session.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expand tokens in <paramref name="template"/> for a specific participant.
|
||||
/// Result is sanitized into NDI-safe characters: alphanumeric, underscore,
|
||||
/// hyphen, period. NDI spec allows more, but a conservative set keeps
|
||||
/// downstream switchers happy.
|
||||
/// </summary>
|
||||
public static string Render(string template, Guid participantId, string displayName)
|
||||
{
|
||||
var safeName = SanitizeForNdi(displayName);
|
||||
var guid = participantId.ToString("N")[..8].ToUpperInvariant();
|
||||
var machine = SanitizeForNdi(Environment.MachineName);
|
||||
var timestamp = DateTimeOffset.Now.ToString("yyyyMMdd_HHmmss");
|
||||
|
||||
var result = template
|
||||
.Replace("{name}", safeName)
|
||||
.Replace("{guid}", guid)
|
||||
.Replace("{machine}", machine)
|
||||
.Replace("{timestamp}", timestamp);
|
||||
|
||||
// Final sanitize on the rendered result — protects against a template
|
||||
// that includes literal characters NDI doesn't accept.
|
||||
return SanitizeForNdi(result);
|
||||
}
|
||||
|
||||
private static string SanitizeForNdi(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return string.Empty;
|
||||
var sb = new StringBuilder(s.Length);
|
||||
foreach (var c in s)
|
||||
{
|
||||
if (char.IsLetterOrDigit(c) || c is '_' or '-' or '.')
|
||||
sb.Append(c);
|
||||
else if (char.IsWhiteSpace(c))
|
||||
sb.Append('_');
|
||||
// else: skip
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue