Stream custom live channels using your own media
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

118 lines
3.5 KiB

using System.Globalization;
using ErsatzTV.Core.Domain.Scheduling;
using YamlDotNet.Serialization;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models;
public class YamlPlayoutScheduleItem : IAlternateScheduleItem
{
public string Name { get; set; }
[YamlMember(Alias = "start_date", ApplyNamingConventions = false)]
public string StartDate { get; set; }
[YamlMember(Alias = "end_date", ApplyNamingConventions = false)]
public string EndDate { get; set; }
public int Priority { get; set; }
public List<YamlPlayoutInstruction> Playout { get; set; } = [];
[YamlIgnore]
public int Index => -Priority;
// schedules are purely date-range based, so every day/month is eligible
[YamlIgnore]
public ICollection<DayOfWeek> DaysOfWeek => Enum.GetValues<DayOfWeek>();
[YamlIgnore]
public ICollection<int> DaysOfMonth => Enumerable.Range(1, 31).ToList();
[YamlIgnore]
public ICollection<int> MonthsOfYear => Enumerable.Range(1, 12).ToList();
[YamlIgnore]
public bool LimitToDateRange => true;
[YamlIgnore]
public int StartMonth => Range.StartMonth;
[YamlIgnore]
public int StartDay => Range.StartDay;
[YamlIgnore]
public int? StartYear => Range.StartYear;
[YamlIgnore]
public int EndMonth => Range.EndMonth;
[YamlIgnore]
public int EndDay => Range.EndDay;
[YamlIgnore]
public int? EndYear => Range.EndYear;
private NormalizedRange Range => NormalizedRange.From(StartDate, EndDate);
private readonly record struct NormalizedRange(
int StartMonth,
int StartDay,
int? StartYear,
int EndMonth,
int EndDay,
int? EndYear)
{
// an impossible specific-year range (year 1) so an invalid schedule never matches a real date
private static readonly NormalizedRange Invalid = new(1, 1, 1, 1, 1, 1);
public static NormalizedRange From(string startValue, string endValue)
{
ParsedDate start = ParsedDate.Parse(startValue);
ParsedDate end = ParsedDate.Parse(endValue);
if (!start.Valid || !end.Valid)
{
return Invalid;
}
// a specific-year range requires a year on both endpoints, otherwise it repeats annually
if (start.Year.HasValue && end.Year.HasValue)
{
return new NormalizedRange(start.Month, start.Day, start.Year, end.Month, end.Day, end.Year);
}
return new NormalizedRange(start.Month, start.Day, null, end.Month, end.Day, null);
}
}
private readonly record struct ParsedDate(bool Valid, int? Year, int Month, int Day)
{
public static ParsedDate Parse(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return default;
}
if (DateOnly.TryParseExact(
value.Trim(),
"yyyy-MM-dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateOnly specific))
{
return new ParsedDate(true, specific.Year, specific.Month, specific.Day);
}
string[] parts = value.Trim().Split('-');
if (parts.Length == 2
&& int.TryParse(parts[0], out int month)
&& int.TryParse(parts[1], out int day))
{
return new ParsedDate(true, null, month, day);
}
return default;
}
}
}