add alternate schedule system (#1105)
* start to add program schedule alternates * edit days of the week * editor improvements * save changes * build playouts using alternate schedules * reset playout as needed * add priority message
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
namespace ErsatzTV.Application.Channels;
|
||||||
|
|
||||||
|
public record GetChannelNameByPlayoutId(int PlayoutId) : IRequest<Option<string>>;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using ErsatzTV.Infrastructure.Data;
|
||||||
|
using ErsatzTV.Infrastructure.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Channels;
|
||||||
|
|
||||||
|
public class GetChannelNameByPlayoutIdHandler : IRequestHandler<GetChannelNameByPlayoutId, Option<string>>
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||||
|
|
||||||
|
public GetChannelNameByPlayoutIdHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||||
|
{
|
||||||
|
_dbContextFactory = dbContextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Option<string>> Handle(GetChannelNameByPlayoutId request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
return await dbContext.Playouts
|
||||||
|
.Include(p => p.Channel)
|
||||||
|
.SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId)
|
||||||
|
.MapT(p => p.Channel.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,6 +75,36 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
|||||||
dbContext.Playouts
|
dbContext.Playouts
|
||||||
.Include(p => p.Channel)
|
.Include(p => p.Channel)
|
||||||
.Include(p => p.Items)
|
.Include(p => p.Items)
|
||||||
|
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(a => a.ProgramSchedule)
|
||||||
|
.ThenInclude(ps => ps.Items)
|
||||||
|
.ThenInclude(psi => psi.Collection)
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(a => a.ProgramSchedule)
|
||||||
|
.ThenInclude(ps => ps.Items)
|
||||||
|
.ThenInclude(psi => psi.MediaItem)
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(a => a.ProgramSchedule)
|
||||||
|
.ThenInclude(ps => ps.Items)
|
||||||
|
.ThenInclude(psi => psi.PreRollFiller)
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(a => a.ProgramSchedule)
|
||||||
|
.ThenInclude(ps => ps.Items)
|
||||||
|
.ThenInclude(psi => psi.MidRollFiller)
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(a => a.ProgramSchedule)
|
||||||
|
.ThenInclude(ps => ps.Items)
|
||||||
|
.ThenInclude(psi => psi.PostRollFiller)
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(a => a.ProgramSchedule)
|
||||||
|
.ThenInclude(ps => ps.Items)
|
||||||
|
.ThenInclude(psi => psi.TailFiller)
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(a => a.ProgramSchedule)
|
||||||
|
.ThenInclude(ps => ps.Items)
|
||||||
|
.ThenInclude(psi => psi.FallbackFiller)
|
||||||
|
|
||||||
.Include(p => p.ProgramScheduleAnchors)
|
.Include(p => p.ProgramScheduleAnchors)
|
||||||
.ThenInclude(a => a.MediaItem)
|
.ThenInclude(a => a.MediaItem)
|
||||||
.Include(p => p.ProgramSchedule)
|
.Include(p => p.ProgramSchedule)
|
||||||
@@ -98,6 +128,7 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
|||||||
.Include(p => p.ProgramSchedule)
|
.Include(p => p.ProgramSchedule)
|
||||||
.ThenInclude(ps => ps.Items)
|
.ThenInclude(ps => ps.Items)
|
||||||
.ThenInclude(psi => psi.FallbackFiller)
|
.ThenInclude(psi => psi.FallbackFiller)
|
||||||
|
|
||||||
.SelectOneAsync(p => p.Id, p => p.Id == buildPlayout.PlayoutId)
|
.SelectOneAsync(p => p.Id, p => p.Id == buildPlayout.PlayoutId)
|
||||||
.Map(o => o.ToValidation<BaseError>("Playout does not exist."));
|
.Map(o => o.ToValidation<BaseError>("Playout does not exist."));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace ErsatzTV.Application.Playouts;
|
||||||
|
|
||||||
|
public record ReplacePlayoutAlternateSchedule(
|
||||||
|
int Id,
|
||||||
|
int Index,
|
||||||
|
int ProgramScheduleId,
|
||||||
|
List<DayOfWeek> DaysOfWeek,
|
||||||
|
List<int> DaysOfMonth,
|
||||||
|
List<int> MonthsOfYear);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using ErsatzTV.Core;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Playouts;
|
||||||
|
|
||||||
|
public record ReplacePlayoutAlternateScheduleItems
|
||||||
|
(int PlayoutId, List<ReplacePlayoutAlternateSchedule> Items) : IRequest<Either<BaseError, Unit>>;
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
using System.Threading.Channels;
|
||||||
|
using ErsatzTV.Core;
|
||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
using ErsatzTV.Core.Scheduling;
|
||||||
|
using ErsatzTV.Infrastructure.Data;
|
||||||
|
using ErsatzTV.Infrastructure.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Playouts;
|
||||||
|
|
||||||
|
public class ReplacePlayoutAlternateScheduleItemsHandler :
|
||||||
|
IRequestHandler<ReplacePlayoutAlternateScheduleItems, Either<BaseError, Unit>>
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||||
|
private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
|
||||||
|
private readonly ILogger<ReplacePlayoutAlternateScheduleItemsHandler> _logger;
|
||||||
|
|
||||||
|
public ReplacePlayoutAlternateScheduleItemsHandler(
|
||||||
|
IDbContextFactory<TvContext> dbContextFactory,
|
||||||
|
ChannelWriter<IBackgroundServiceRequest> channel,
|
||||||
|
ILogger<ReplacePlayoutAlternateScheduleItemsHandler> logger)
|
||||||
|
{
|
||||||
|
_dbContextFactory = dbContextFactory;
|
||||||
|
_channel = channel;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Either<BaseError, Unit>> Handle(
|
||||||
|
ReplacePlayoutAlternateScheduleItems request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// TODO: validate that items is not empty
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
|
Option<Playout> maybePlayout = await dbContext.Playouts
|
||||||
|
.Include(p => p.ProgramSchedule)
|
||||||
|
.Include(p => p.ProgramScheduleAlternates)
|
||||||
|
.ThenInclude(p => p.ProgramSchedule)
|
||||||
|
.SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId);
|
||||||
|
|
||||||
|
foreach (Playout playout in maybePlayout)
|
||||||
|
{
|
||||||
|
ProgramSchedule existingDefault = playout.ProgramSchedule;
|
||||||
|
|
||||||
|
// exclude highest index
|
||||||
|
int maxIndex = request.Items.Map(x => x.Index).Max();
|
||||||
|
ReplacePlayoutAlternateSchedule highest = request.Items.First(x => x.Index == maxIndex);
|
||||||
|
|
||||||
|
ProgramScheduleAlternate[] existing = playout.ProgramScheduleAlternates.ToArray();
|
||||||
|
|
||||||
|
var incoming = request.Items.Except(new[] { highest }).ToList();
|
||||||
|
|
||||||
|
var toAdd = incoming.Filter(x => existing.All(e => e.Id != x.Id)).ToList();
|
||||||
|
var toRemove = existing.Filter(e => incoming.All(m => m.Id != e.Id)).ToList();
|
||||||
|
var toUpdate = incoming.Except(toAdd).ToList();
|
||||||
|
|
||||||
|
playout.ProgramScheduleAlternates.RemoveAll(toRemove.Contains);
|
||||||
|
|
||||||
|
foreach (ReplacePlayoutAlternateSchedule add in toAdd)
|
||||||
|
{
|
||||||
|
playout.ProgramScheduleAlternates.Add(
|
||||||
|
new ProgramScheduleAlternate
|
||||||
|
{
|
||||||
|
PlayoutId = playout.Id,
|
||||||
|
Index = add.Index,
|
||||||
|
ProgramScheduleId = add.ProgramScheduleId,
|
||||||
|
DaysOfWeek = add.DaysOfWeek,
|
||||||
|
DaysOfMonth = add.DaysOfMonth,
|
||||||
|
MonthsOfYear = add.MonthsOfYear
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (ReplacePlayoutAlternateSchedule update in toUpdate)
|
||||||
|
{
|
||||||
|
foreach (ProgramScheduleAlternate ex in existing.Filter(x => x.Id == update.Id))
|
||||||
|
{
|
||||||
|
ex.Index = update.Index;
|
||||||
|
ex.ProgramScheduleId = update.ProgramScheduleId;
|
||||||
|
ex.DaysOfWeek = update.DaysOfWeek;
|
||||||
|
ex.DaysOfMonth = update.DaysOfMonth;
|
||||||
|
ex.MonthsOfYear = update.MonthsOfYear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save highest index directly to playout
|
||||||
|
if (playout.ProgramScheduleId != highest.ProgramScheduleId)
|
||||||
|
{
|
||||||
|
playout.ProgramScheduleId = highest.ProgramScheduleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
await dbContext.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
Option<PlayoutItem> maybePlayoutItem = await dbContext.PlayoutItems
|
||||||
|
.Filter(pi => pi.PlayoutId == request.PlayoutId)
|
||||||
|
.OrderByDescending(pi => pi.Start)
|
||||||
|
.FirstOrDefaultAsync(cancellationToken)
|
||||||
|
.Map(Optional);
|
||||||
|
|
||||||
|
foreach (PlayoutItem playoutItem in maybePlayoutItem)
|
||||||
|
{
|
||||||
|
DateTimeOffset start = DateTimeOffset.Now;
|
||||||
|
var daysToCheck = Enumerable.Range(0, (playoutItem.StartOffset - start).Days + 1)
|
||||||
|
.Select(d => start.AddDays(d))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (DateTimeOffset dayToCheck in daysToCheck)
|
||||||
|
{
|
||||||
|
ProgramSchedule oldSchedule = PlayoutScheduleSelector.GetProgramScheduleFor(
|
||||||
|
existingDefault,
|
||||||
|
existing,
|
||||||
|
dayToCheck);
|
||||||
|
|
||||||
|
ProgramSchedule newSchedule = PlayoutScheduleSelector.GetProgramScheduleFor(
|
||||||
|
playout.ProgramSchedule,
|
||||||
|
playout.ProgramScheduleAlternates,
|
||||||
|
dayToCheck);
|
||||||
|
|
||||||
|
if (oldSchedule.Id != newSchedule.Id)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Alternate schedule change detected for day {Day}, schedule {One} => {Two}; will refresh playout",
|
||||||
|
dayToCheck,
|
||||||
|
oldSchedule.Name,
|
||||||
|
newSchedule.Name);
|
||||||
|
|
||||||
|
await _channel.WriteAsync(
|
||||||
|
new BuildPlayout(request.PlayoutId, PlayoutBuildMode.Refresh),
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unit.Default;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BaseError.New(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,16 @@ internal static class Mapper
|
|||||||
playoutItem.StartOffset,
|
playoutItem.StartOffset,
|
||||||
GetDisplayDuration(playoutItem.FinishOffset - playoutItem.StartOffset));
|
GetDisplayDuration(playoutItem.FinishOffset - playoutItem.StartOffset));
|
||||||
|
|
||||||
|
internal static PlayoutAlternateScheduleViewModel ProjectToViewModel(
|
||||||
|
ProgramScheduleAlternate programScheduleAlternate) =>
|
||||||
|
new(
|
||||||
|
programScheduleAlternate.Id,
|
||||||
|
programScheduleAlternate.Index,
|
||||||
|
programScheduleAlternate.ProgramScheduleId,
|
||||||
|
programScheduleAlternate.DaysOfWeek,
|
||||||
|
programScheduleAlternate.DaysOfMonth,
|
||||||
|
programScheduleAlternate.MonthsOfYear);
|
||||||
|
|
||||||
private static string GetDisplayTitle(PlayoutItem playoutItem)
|
private static string GetDisplayTitle(PlayoutItem playoutItem)
|
||||||
{
|
{
|
||||||
switch (playoutItem.MediaItem)
|
switch (playoutItem.MediaItem)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace ErsatzTV.Application.Playouts;
|
||||||
|
|
||||||
|
public record PlayoutAlternateScheduleViewModel(
|
||||||
|
int Id,
|
||||||
|
int Index,
|
||||||
|
int ProgramScheduleId,
|
||||||
|
ICollection<DayOfWeek> DaysOfWeek,
|
||||||
|
ICollection<int> DaysOfMonth,
|
||||||
|
ICollection<int> MonthsOfYear);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace ErsatzTV.Application.Playouts;
|
||||||
|
|
||||||
|
public record GetPlayoutAlternateSchedules(int PlayoutId) : IRequest<List<PlayoutAlternateScheduleViewModel>>;
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
using ErsatzTV.Infrastructure.Data;
|
||||||
|
using ErsatzTV.Infrastructure.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using static ErsatzTV.Application.Playouts.Mapper;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Playouts;
|
||||||
|
|
||||||
|
public class GetPlayoutAlternateSchedulesHandler :
|
||||||
|
IRequestHandler<GetPlayoutAlternateSchedules, List<PlayoutAlternateScheduleViewModel>>
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||||
|
|
||||||
|
public GetPlayoutAlternateSchedulesHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||||
|
_dbContextFactory = dbContextFactory;
|
||||||
|
|
||||||
|
public async Task<List<PlayoutAlternateScheduleViewModel>> Handle(
|
||||||
|
GetPlayoutAlternateSchedules request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
|
List<PlayoutAlternateScheduleViewModel> result = await dbContext.ProgramScheduleAlternates
|
||||||
|
.Filter(psa => psa.PlayoutId == request.PlayoutId)
|
||||||
|
.Include(psa => psa.ProgramSchedule)
|
||||||
|
.Map(psa => ProjectToViewModel(psa))
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
Option<ProgramSchedule> maybeDefaultSchedule = await dbContext.Playouts
|
||||||
|
.Include(p => p.ProgramSchedule)
|
||||||
|
.SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId)
|
||||||
|
.MapT(p => p.ProgramSchedule);
|
||||||
|
|
||||||
|
foreach (ProgramSchedule defaultSchedule in maybeDefaultSchedule)
|
||||||
|
{
|
||||||
|
var psa = new ProgramScheduleAlternate
|
||||||
|
{
|
||||||
|
Id = -1,
|
||||||
|
PlayoutId = request.PlayoutId,
|
||||||
|
ProgramScheduleId = defaultSchedule.Id,
|
||||||
|
ProgramSchedule = defaultSchedule,
|
||||||
|
Index = result.Map(i => i.Index).DefaultIfEmpty().Max() + 1,
|
||||||
|
DaysOfMonth = ProgramScheduleAlternate.AllDaysOfMonth(),
|
||||||
|
DaysOfWeek = ProgramScheduleAlternate.AllDaysOfWeek(),
|
||||||
|
MonthsOfYear = ProgramScheduleAlternate.AllMonthsOfYear()
|
||||||
|
};
|
||||||
|
|
||||||
|
result.Add(ProjectToViewModel(psa));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -543,7 +543,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -639,7 +640,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -783,7 +785,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -885,7 +888,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -996,7 +1000,8 @@ public class PlayoutBuilderTests
|
|||||||
InFlood = true
|
InFlood = true
|
||||||
},
|
},
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1100,7 +1105,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1208,7 +1214,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1321,7 +1328,8 @@ public class PlayoutBuilderTests
|
|||||||
MultipleRemaining = 2
|
MultipleRemaining = 2
|
||||||
},
|
},
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1423,7 +1431,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1536,7 +1545,8 @@ public class PlayoutBuilderTests
|
|||||||
DurationFinish = HoursAfterMidnight(3).UtcDateTime
|
DurationFinish = HoursAfterMidnight(3).UtcDateTime
|
||||||
},
|
},
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1660,7 +1670,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1776,7 +1787,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -1852,7 +1864,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -2049,7 +2062,8 @@ public class PlayoutBuilderTests
|
|||||||
},
|
},
|
||||||
|
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
playout.ProgramScheduleAnchors.Add(
|
playout.ProgramScheduleAnchors.Add(
|
||||||
@@ -2437,7 +2451,8 @@ public class PlayoutBuilderTests
|
|||||||
InFlood = true
|
InFlood = true
|
||||||
},
|
},
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -2548,7 +2563,8 @@ public class PlayoutBuilderTests
|
|||||||
MultipleRemaining = 2
|
MultipleRemaining = 2
|
||||||
},
|
},
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -2659,7 +2675,8 @@ public class PlayoutBuilderTests
|
|||||||
DurationFinish = HoursAfterMidnight(3).UtcDateTime
|
DurationFinish = HoursAfterMidnight(3).UtcDateTime
|
||||||
},
|
},
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
Items = new List<PlayoutItem>()
|
Items = new List<PlayoutItem>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var configRepo = new Mock<IConfigElementRepository>();
|
var configRepo = new Mock<IConfigElementRepository>();
|
||||||
@@ -2768,7 +2785,8 @@ public class PlayoutBuilderTests
|
|||||||
ProgramSchedule = new ProgramSchedule { Items = items },
|
ProgramSchedule = new ProgramSchedule { Items = items },
|
||||||
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
||||||
Items = new List<PlayoutItem>(),
|
Items = new List<PlayoutItem>(),
|
||||||
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>()
|
ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>(),
|
||||||
|
ProgramScheduleAlternates = new List<ProgramScheduleAlternate>()
|
||||||
};
|
};
|
||||||
|
|
||||||
return new TestData(builder, playout);
|
return new TestData(builder, playout);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public class Playout
|
|||||||
public Channel Channel { get; set; }
|
public Channel Channel { get; set; }
|
||||||
public int ProgramScheduleId { get; set; }
|
public int ProgramScheduleId { get; set; }
|
||||||
public ProgramSchedule ProgramSchedule { get; set; }
|
public ProgramSchedule ProgramSchedule { get; set; }
|
||||||
|
public List<ProgramScheduleAlternate> ProgramScheduleAlternates { get; set; }
|
||||||
public ProgramSchedulePlayoutType ProgramSchedulePlayoutType { get; set; }
|
public ProgramSchedulePlayoutType ProgramSchedulePlayoutType { get; set; }
|
||||||
public List<PlayoutItem> Items { get; set; }
|
public List<PlayoutItem> Items { get; set; }
|
||||||
public PlayoutAnchor Anchor { get; set; }
|
public PlayoutAnchor Anchor { get; set; }
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ public class ProgramSchedule
|
|||||||
public bool RandomStartPoint { get; set; }
|
public bool RandomStartPoint { get; set; }
|
||||||
public List<ProgramScheduleItem> Items { get; set; }
|
public List<ProgramScheduleItem> Items { get; set; }
|
||||||
public List<Playout> Playouts { get; set; }
|
public List<Playout> Playouts { get; set; }
|
||||||
|
public List<ProgramScheduleAlternate> ProgramScheduleAlternates { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace ErsatzTV.Core.Domain;
|
||||||
|
|
||||||
|
public class ProgramScheduleAlternate
|
||||||
|
{
|
||||||
|
public static List<DayOfWeek> AllDaysOfWeek() => new()
|
||||||
|
{
|
||||||
|
DayOfWeek.Monday,
|
||||||
|
DayOfWeek.Tuesday,
|
||||||
|
DayOfWeek.Wednesday,
|
||||||
|
DayOfWeek.Thursday,
|
||||||
|
DayOfWeek.Friday,
|
||||||
|
DayOfWeek.Saturday,
|
||||||
|
DayOfWeek.Sunday
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<int> AllDaysOfMonth() => Enumerable.Range(1, 31).ToList();
|
||||||
|
public static List<int> AllMonthsOfYear() => Enumerable.Range(1, 12).ToList();
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int PlayoutId { get; set; }
|
||||||
|
public Playout Playout { get; set; }
|
||||||
|
public int ProgramScheduleId { get; set; }
|
||||||
|
public ProgramSchedule ProgramSchedule { get; set; }
|
||||||
|
public int Index { get; set; }
|
||||||
|
public ICollection<DayOfWeek> DaysOfWeek { get; set; }
|
||||||
|
public ICollection<int> DaysOfMonth { get; set; }
|
||||||
|
public ICollection<int> MonthsOfYear { get; set; }
|
||||||
|
}
|
||||||
@@ -203,7 +203,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
parameters.Start,
|
parameters.Start,
|
||||||
parameters.Finish,
|
parameters.Finish,
|
||||||
parameters.CollectionMediaItems,
|
parameters.CollectionMediaItems,
|
||||||
playout.ProgramSchedule.RandomStartPoint);
|
true);
|
||||||
|
|
||||||
return playout;
|
return playout;
|
||||||
}
|
}
|
||||||
@@ -237,11 +237,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
Map<CollectionKey, List<MediaItem>> collectionMediaItems = await GetCollectionMediaItems(playout);
|
Map<CollectionKey, List<MediaItem>> collectionMediaItems = await GetCollectionMediaItems(playout);
|
||||||
if (!collectionMediaItems.Any())
|
if (!collectionMediaItems.Any())
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning("Playout {Playout} has no items", playout.Channel.Name);
|
||||||
"Playout {Playout} schedule {Schedule} has no items",
|
|
||||||
playout.Channel.Name,
|
|
||||||
playout.ProgramSchedule.Name);
|
|
||||||
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,13 +361,21 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
bool saveAnchorDate,
|
bool saveAnchorDate,
|
||||||
bool randomStartPoint)
|
bool randomStartPoint)
|
||||||
{
|
{
|
||||||
var sortedScheduleItems = playout.ProgramSchedule.Items.OrderBy(i => i.Index).ToList();
|
ProgramSchedule activeSchedule = PlayoutScheduleSelector.GetProgramScheduleFor(
|
||||||
|
playout.ProgramSchedule,
|
||||||
|
playout.ProgramScheduleAlternates,
|
||||||
|
playoutStart);
|
||||||
|
|
||||||
|
// random start points are disabled in some scenarios, so ensure it's enabled and active
|
||||||
|
randomStartPoint = randomStartPoint && activeSchedule.RandomStartPoint;
|
||||||
|
|
||||||
|
var sortedScheduleItems = activeSchedule.Items.OrderBy(i => i.Index).ToList();
|
||||||
CollectionEnumeratorState scheduleItemsEnumeratorState =
|
CollectionEnumeratorState scheduleItemsEnumeratorState =
|
||||||
playout.Anchor?.ScheduleItemsEnumeratorState ?? new CollectionEnumeratorState
|
playout.Anchor?.ScheduleItemsEnumeratorState ?? new CollectionEnumeratorState
|
||||||
{ Seed = Random.Next(), Index = 0 };
|
{ Seed = Random.Next(), Index = 0 };
|
||||||
IScheduleItemsEnumerator scheduleItemsEnumerator = playout.ProgramSchedule.ShuffleScheduleItems
|
IScheduleItemsEnumerator scheduleItemsEnumerator = activeSchedule.ShuffleScheduleItems
|
||||||
? new ShuffledScheduleItemsEnumerator(playout.ProgramSchedule.Items, scheduleItemsEnumeratorState)
|
? new ShuffledScheduleItemsEnumerator(activeSchedule.Items, scheduleItemsEnumeratorState)
|
||||||
: new OrderedScheduleItemsEnumerator(playout.ProgramSchedule.Items, scheduleItemsEnumeratorState);
|
: new OrderedScheduleItemsEnumerator(activeSchedule.Items, scheduleItemsEnumeratorState);
|
||||||
var collectionEnumerators = new Dictionary<CollectionKey, IMediaCollectionEnumerator>();
|
var collectionEnumerators = new Dictionary<CollectionKey, IMediaCollectionEnumerator>();
|
||||||
foreach ((CollectionKey collectionKey, List<MediaItem> mediaItems) in collectionMediaItems)
|
foreach ((CollectionKey collectionKey, List<MediaItem> mediaItems) in collectionMediaItems)
|
||||||
{
|
{
|
||||||
@@ -381,7 +385,13 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
PlaybackOrder playbackOrder = maybeScheduleItem
|
PlaybackOrder playbackOrder = maybeScheduleItem
|
||||||
.Match(item => item.PlaybackOrder, () => PlaybackOrder.Shuffle);
|
.Match(item => item.PlaybackOrder, () => PlaybackOrder.Shuffle);
|
||||||
IMediaCollectionEnumerator enumerator =
|
IMediaCollectionEnumerator enumerator =
|
||||||
await GetMediaCollectionEnumerator(playout, collectionKey, mediaItems, playbackOrder, randomStartPoint);
|
await GetMediaCollectionEnumerator(
|
||||||
|
playout,
|
||||||
|
activeSchedule,
|
||||||
|
collectionKey,
|
||||||
|
mediaItems,
|
||||||
|
playbackOrder,
|
||||||
|
randomStartPoint);
|
||||||
collectionEnumerators.Add(collectionKey, enumerator);
|
collectionEnumerators.Add(collectionKey, enumerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,7 +543,11 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
// build program schedule anchors
|
// build program schedule anchors
|
||||||
playout.ProgramScheduleAnchors = BuildProgramScheduleAnchors(playout, collectionEnumerators, saveAnchorDate);
|
playout.ProgramScheduleAnchors = BuildProgramScheduleAnchors(
|
||||||
|
playout,
|
||||||
|
activeSchedule,
|
||||||
|
collectionEnumerators,
|
||||||
|
saveAnchorDate);
|
||||||
|
|
||||||
return playout;
|
return playout;
|
||||||
}
|
}
|
||||||
@@ -541,6 +555,8 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
private async Task<Map<CollectionKey, List<MediaItem>>> GetCollectionMediaItems(Playout playout)
|
private async Task<Map<CollectionKey, List<MediaItem>>> GetCollectionMediaItems(Playout playout)
|
||||||
{
|
{
|
||||||
var collectionKeys = playout.ProgramSchedule.Items
|
var collectionKeys = playout.ProgramSchedule.Items
|
||||||
|
.Append(playout.ProgramScheduleAlternates.Bind(psa => psa.ProgramSchedule.Items))
|
||||||
|
.DistinctBy(i => i.Id)
|
||||||
.SelectMany(CollectionKeysForItem)
|
.SelectMany(CollectionKeysForItem)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -640,6 +656,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
|
|
||||||
private static List<PlayoutProgramScheduleAnchor> BuildProgramScheduleAnchors(
|
private static List<PlayoutProgramScheduleAnchor> BuildProgramScheduleAnchors(
|
||||||
Playout playout,
|
Playout playout,
|
||||||
|
ProgramSchedule activeSchedule,
|
||||||
Dictionary<CollectionKey, IMediaCollectionEnumerator> collectionEnumerators,
|
Dictionary<CollectionKey, IMediaCollectionEnumerator> collectionEnumerators,
|
||||||
bool saveAnchorDate)
|
bool saveAnchorDate)
|
||||||
{
|
{
|
||||||
@@ -665,8 +682,8 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
{
|
{
|
||||||
Playout = playout,
|
Playout = playout,
|
||||||
PlayoutId = playout.Id,
|
PlayoutId = playout.Id,
|
||||||
ProgramSchedule = playout.ProgramSchedule,
|
ProgramSchedule = activeSchedule,
|
||||||
ProgramScheduleId = playout.ProgramScheduleId,
|
ProgramScheduleId = activeSchedule.Id,
|
||||||
CollectionType = collectionKey.CollectionType,
|
CollectionType = collectionKey.CollectionType,
|
||||||
CollectionId = collectionKey.CollectionId,
|
CollectionId = collectionKey.CollectionId,
|
||||||
MultiCollectionId = collectionKey.MultiCollectionId,
|
MultiCollectionId = collectionKey.MultiCollectionId,
|
||||||
@@ -694,6 +711,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
|
|
||||||
private async Task<IMediaCollectionEnumerator> GetMediaCollectionEnumerator(
|
private async Task<IMediaCollectionEnumerator> GetMediaCollectionEnumerator(
|
||||||
Playout playout,
|
Playout playout,
|
||||||
|
ProgramSchedule activeSchedule,
|
||||||
CollectionKey collectionKey,
|
CollectionKey collectionKey,
|
||||||
List<MediaItem> mediaItems,
|
List<MediaItem> mediaItems,
|
||||||
PlaybackOrder playbackOrder,
|
PlaybackOrder playbackOrder,
|
||||||
@@ -702,7 +720,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
Option<PlayoutProgramScheduleAnchor> maybeAnchor = playout.ProgramScheduleAnchors
|
Option<PlayoutProgramScheduleAnchor> maybeAnchor = playout.ProgramScheduleAnchors
|
||||||
.OrderByDescending(a => a.AnchorDate ?? DateTime.MaxValue)
|
.OrderByDescending(a => a.AnchorDate ?? DateTime.MaxValue)
|
||||||
.FirstOrDefault(
|
.FirstOrDefault(
|
||||||
a => a.ProgramScheduleId == playout.ProgramScheduleId
|
a => a.ProgramScheduleId == activeSchedule.Id
|
||||||
&& a.CollectionType == collectionKey.CollectionType
|
&& a.CollectionType == collectionKey.CollectionType
|
||||||
&& a.CollectionId == collectionKey.CollectionId
|
&& a.CollectionId == collectionKey.CollectionId
|
||||||
&& a.MultiCollectionId == collectionKey.MultiCollectionId
|
&& a.MultiCollectionId == collectionKey.MultiCollectionId
|
||||||
@@ -757,7 +775,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
return new ShuffleInOrderCollectionEnumerator(
|
return new ShuffleInOrderCollectionEnumerator(
|
||||||
await GetCollectionItemsForShuffleInOrder(collectionKey),
|
await GetCollectionItemsForShuffleInOrder(collectionKey),
|
||||||
state,
|
state,
|
||||||
playout.ProgramSchedule.RandomStartPoint);
|
activeSchedule.RandomStartPoint);
|
||||||
case PlaybackOrder.MultiEpisodeShuffle when
|
case PlaybackOrder.MultiEpisodeShuffle when
|
||||||
collectionKey.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow &&
|
collectionKey.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow &&
|
||||||
collectionKey.MediaItemId.HasValue:
|
collectionKey.MediaItemId.HasValue:
|
||||||
@@ -795,7 +813,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
case PlaybackOrder.MultiEpisodeShuffle:
|
case PlaybackOrder.MultiEpisodeShuffle:
|
||||||
case PlaybackOrder.Shuffle:
|
case PlaybackOrder.Shuffle:
|
||||||
return new ShuffledMediaCollectionEnumerator(
|
return new ShuffledMediaCollectionEnumerator(
|
||||||
await GetGroupedMediaItemsForShuffle(playout, mediaItems, collectionKey),
|
await GetGroupedMediaItemsForShuffle(activeSchedule, mediaItems, collectionKey),
|
||||||
state);
|
state);
|
||||||
default:
|
default:
|
||||||
// TODO: handle this error case differently?
|
// TODO: handle this error case differently?
|
||||||
@@ -804,7 +822,7 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<GroupedMediaItem>> GetGroupedMediaItemsForShuffle(
|
private async Task<List<GroupedMediaItem>> GetGroupedMediaItemsForShuffle(
|
||||||
Playout playout,
|
ProgramSchedule activeSchedule,
|
||||||
List<MediaItem> mediaItems,
|
List<MediaItem> mediaItems,
|
||||||
CollectionKey collectionKey)
|
CollectionKey collectionKey)
|
||||||
{
|
{
|
||||||
@@ -816,10 +834,8 @@ public class PlayoutBuilder : IPlayoutBuilder
|
|||||||
return MultiCollectionGrouper.GroupMediaItems(collections);
|
return MultiCollectionGrouper.GroupMediaItems(collections);
|
||||||
}
|
}
|
||||||
|
|
||||||
return playout.ProgramSchedule.KeepMultiPartEpisodesTogether
|
return activeSchedule.KeepMultiPartEpisodesTogether
|
||||||
? MultiPartEpisodeGrouper.GroupMediaItems(
|
? MultiPartEpisodeGrouper.GroupMediaItems(mediaItems, activeSchedule.TreatCollectionsAsShows)
|
||||||
mediaItems,
|
|
||||||
playout.ProgramSchedule.TreatCollectionsAsShows)
|
|
||||||
: mediaItems.Map(mi => new GroupedMediaItem(mi, null)).ToList();
|
: mediaItems.Map(mi => new GroupedMediaItem(mi, null)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Scheduling;
|
||||||
|
|
||||||
|
public static class PlayoutScheduleSelector
|
||||||
|
{
|
||||||
|
public static ProgramSchedule GetProgramScheduleFor(
|
||||||
|
ProgramSchedule defaultSchedule,
|
||||||
|
IEnumerable<ProgramScheduleAlternate> alternates,
|
||||||
|
DateTimeOffset date)
|
||||||
|
{
|
||||||
|
foreach (ProgramScheduleAlternate alternate in alternates.OrderBy(x => x.Index))
|
||||||
|
{
|
||||||
|
bool daysOfWeek = alternate.DaysOfWeek.Count is 0 or 7 ||
|
||||||
|
alternate.DaysOfWeek.Contains(date.DayOfWeek);
|
||||||
|
|
||||||
|
if (!daysOfWeek)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool daysOfMonth = alternate.DaysOfMonth.Count is 0 or 31 ||
|
||||||
|
alternate.DaysOfMonth.Contains(date.Day);
|
||||||
|
if (!daysOfMonth)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool monthOfYear = alternate.MonthsOfYear.Count is 0 or 12 ||
|
||||||
|
alternate.MonthsOfYear.Contains(date.Month);
|
||||||
|
|
||||||
|
if (monthOfYear)
|
||||||
|
{
|
||||||
|
return alternate.ProgramSchedule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultSchedule;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Data.Configurations;
|
||||||
|
|
||||||
|
public class CollectionValueComparer<T> : ValueComparer<ICollection<T>>
|
||||||
|
{
|
||||||
|
public CollectionValueComparer() : base(
|
||||||
|
(c1, c2) => c1.SequenceEqual(c2),
|
||||||
|
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
|
||||||
|
c => c.ToHashSet())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Data.Configurations;
|
||||||
|
|
||||||
|
public class EnumCollectionJsonValueConverter<T> : ValueConverter<ICollection<T>, string> where T : Enum
|
||||||
|
{
|
||||||
|
public EnumCollectionJsonValueConverter() : base(
|
||||||
|
v => JsonConvert.SerializeObject(v.Select(e => e.ToString()).ToList()),
|
||||||
|
v => JsonConvert.DeserializeObject<ICollection<string>>(v)
|
||||||
|
.Select(e => (T)Enum.Parse(typeof(T), e)).ToList())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,11 @@ public class PlayoutConfiguration : IEntityTypeConfiguration<Playout>
|
|||||||
{
|
{
|
||||||
builder.ToTable("Playout");
|
builder.ToTable("Playout");
|
||||||
|
|
||||||
|
builder.HasMany(p => p.ProgramScheduleAlternates)
|
||||||
|
.WithOne(a => a.Playout)
|
||||||
|
.HasForeignKey(a => a.PlayoutId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
builder.HasMany(p => p.Items)
|
builder.HasMany(p => p.Items)
|
||||||
.WithOne(pi => pi.Playout)
|
.WithOne(pi => pi.Playout)
|
||||||
.HasForeignKey(pi => pi.PlayoutId)
|
.HasForeignKey(pi => pi.PlayoutId)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Data.Configurations;
|
||||||
|
|
||||||
|
public class ProgramScheduleAlternateConfiguration : IEntityTypeConfiguration<ProgramScheduleAlternate>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<ProgramScheduleAlternate> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable("ProgramScheduleAlternate");
|
||||||
|
|
||||||
|
var intCollectionValueConverter = new ValueConverter<ICollection<int>, string>(
|
||||||
|
i => string.Join(",", i),
|
||||||
|
s => string.IsNullOrWhiteSpace(s)
|
||||||
|
? Array.Empty<int>()
|
||||||
|
: s.Split(new[] { ',' }).Select(int.Parse).ToArray());
|
||||||
|
|
||||||
|
var intCollectionValueComparer = new CollectionValueComparer<int>();
|
||||||
|
|
||||||
|
builder.Property(t => t.DaysOfMonth)
|
||||||
|
.HasConversion(intCollectionValueConverter)
|
||||||
|
.Metadata.SetValueComparer(intCollectionValueComparer);
|
||||||
|
|
||||||
|
builder.Property(t => t.MonthsOfYear)
|
||||||
|
.HasConversion(intCollectionValueConverter)
|
||||||
|
.Metadata.SetValueComparer(intCollectionValueComparer);
|
||||||
|
|
||||||
|
builder.Property(t => t.DaysOfWeek)
|
||||||
|
.HasConversion(new EnumCollectionJsonValueConverter<DayOfWeek>())
|
||||||
|
.Metadata.SetValueComparer(new CollectionValueComparer<DayOfWeek>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,5 +22,10 @@ public class ProgramScheduleConfiguration : IEntityTypeConfiguration<ProgramSche
|
|||||||
.WithOne(p => p.ProgramSchedule)
|
.WithOne(p => p.ProgramSchedule)
|
||||||
.HasForeignKey(p => p.ProgramScheduleId)
|
.HasForeignKey(p => p.ProgramScheduleId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.HasMany(p => p.ProgramScheduleAlternates)
|
||||||
|
.WithOne(a => a.ProgramSchedule)
|
||||||
|
.HasForeignKey(a => a.ProgramScheduleId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public class TvContext : DbContext
|
|||||||
public DbSet<ProgramSchedule> ProgramSchedules { get; set; }
|
public DbSet<ProgramSchedule> ProgramSchedules { get; set; }
|
||||||
public DbSet<ProgramScheduleItem> ProgramScheduleItems { get; set; }
|
public DbSet<ProgramScheduleItem> ProgramScheduleItems { get; set; }
|
||||||
public DbSet<Playout> Playouts { get; set; }
|
public DbSet<Playout> Playouts { get; set; }
|
||||||
|
public DbSet<ProgramScheduleAlternate> ProgramScheduleAlternates { get; set; }
|
||||||
public DbSet<PlayoutItem> PlayoutItems { get; set; }
|
public DbSet<PlayoutItem> PlayoutItems { get; set; }
|
||||||
public DbSet<PlayoutProgramScheduleAnchor> PlayoutProgramScheduleItemAnchors { get; set; }
|
public DbSet<PlayoutProgramScheduleAnchor> PlayoutProgramScheduleItemAnchors { get; set; }
|
||||||
public DbSet<FFmpegProfile> FFmpegProfiles { get; set; }
|
public DbSet<FFmpegProfile> FFmpegProfiles { get; set; }
|
||||||
|
|||||||
Generated
+4394
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddProgramScheduleAlternates : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ProgramScheduleAlternate",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
PlayoutId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
ProgramScheduleId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Index = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DaysOfWeek = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
DaysOfMonth = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
MonthsOfYear = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ProgramScheduleAlternate", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProgramScheduleAlternate_Playout_PlayoutId",
|
||||||
|
column: x => x.PlayoutId,
|
||||||
|
principalTable: "Playout",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ProgramScheduleAlternate_ProgramSchedule_ProgramScheduleId",
|
||||||
|
column: x => x.ProgramScheduleId,
|
||||||
|
principalTable: "ProgramSchedule",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ProgramScheduleAlternate_PlayoutId",
|
||||||
|
table: "ProgramScheduleAlternate",
|
||||||
|
column: "PlayoutId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ProgramScheduleAlternate_ProgramScheduleId",
|
||||||
|
table: "ProgramScheduleAlternate",
|
||||||
|
column: "ProgramScheduleId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ProgramScheduleAlternate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.10");
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.1");
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
|
||||||
{
|
{
|
||||||
@@ -820,6 +820,8 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.HasIndex("MediaSourceId");
|
b.HasIndex("MediaSourceId");
|
||||||
|
|
||||||
b.ToTable("Library", (string)null);
|
b.ToTable("Library", (string)null);
|
||||||
|
|
||||||
|
b.UseTptMappingStrategy();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryFolder", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.LibraryFolder", b =>
|
||||||
@@ -914,6 +916,8 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("MediaFile", (string)null);
|
b.ToTable("MediaFile", (string)null);
|
||||||
|
|
||||||
|
b.UseTptMappingStrategy();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
|
||||||
@@ -933,6 +937,8 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.HasIndex("LibraryPathId");
|
b.HasIndex("LibraryPathId");
|
||||||
|
|
||||||
b.ToTable("MediaItem", (string)null);
|
b.ToTable("MediaItem", (string)null);
|
||||||
|
|
||||||
|
b.UseTptMappingStrategy();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
|
||||||
@@ -944,6 +950,8 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("MediaSource", (string)null);
|
b.ToTable("MediaSource", (string)null);
|
||||||
|
|
||||||
|
b.UseTptMappingStrategy();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaStream", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaStream", b =>
|
||||||
@@ -1598,6 +1606,39 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("ProgramSchedule", (string)null);
|
b.ToTable("ProgramSchedule", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleAlternate", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("DaysOfMonth")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DaysOfWeek")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Index")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("MonthsOfYear")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("PlayoutId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ProgramScheduleId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlayoutId");
|
||||||
|
|
||||||
|
b.HasIndex("ProgramScheduleId");
|
||||||
|
|
||||||
|
b.ToTable("ProgramScheduleAlternate", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -1692,6 +1733,8 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.HasIndex("WatermarkId");
|
b.HasIndex("WatermarkId");
|
||||||
|
|
||||||
b.ToTable("ProgramScheduleItem", (string)null);
|
b.ToTable("ProgramScheduleItem", (string)null);
|
||||||
|
|
||||||
|
b.UseTptMappingStrategy();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b =>
|
||||||
@@ -2265,13 +2308,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("JellyfinPathInfo");
|
b.ToTable("JellyfinPathInfo");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Artist", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
|
||||||
|
|
||||||
b.ToTable("Artist", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyLibrary", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
||||||
@@ -2285,31 +2321,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("EmbyLibrary", (string)null);
|
b.ToTable("EmbyLibrary", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMediaSource", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
|
||||||
|
|
||||||
b.Property<string>("OperatingSystem")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ServerName")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("EmbyMediaSource", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
|
||||||
|
|
||||||
b.Property<int>("SeasonId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.HasIndex("SeasonId");
|
|
||||||
|
|
||||||
b.ToTable("Episode", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinLibrary", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
||||||
@@ -2323,19 +2334,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("JellyfinLibrary", (string)null);
|
b.ToTable("JellyfinLibrary", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMediaSource", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
|
||||||
|
|
||||||
b.Property<string>("OperatingSystem")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ServerName")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("JellyfinMediaSource", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
||||||
@@ -2343,11 +2341,49 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("LocalLibrary", (string)null);
|
b.ToTable("LocalLibrary", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
||||||
|
|
||||||
b.ToTable("LocalMediaSource", (string)null);
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("ShouldSyncItems")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.ToTable("PlexLibrary", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaFile");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("PlexId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.ToTable("PlexMediaFile", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Artist", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
||||||
|
|
||||||
|
b.ToTable("Artist", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasIndex("SeasonId");
|
||||||
|
|
||||||
|
b.ToTable("Episode", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
|
||||||
@@ -2376,30 +2412,66 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("OtherVideo", (string)null);
|
b.ToTable("OtherVideo", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Library");
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
||||||
|
|
||||||
b.Property<string>("Key")
|
b.Property<int>("SeasonNumber")
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<bool>("ShouldSyncItems")
|
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.ToTable("PlexLibrary", (string)null);
|
b.Property<int>("ShowId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasIndex("ShowId");
|
||||||
|
|
||||||
|
b.ToTable("Season", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaFile");
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
||||||
|
|
||||||
b.Property<string>("Key")
|
b.ToTable("Show", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Song", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
||||||
|
|
||||||
|
b.ToTable("Song", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
||||||
|
|
||||||
|
b.Property<string>("OperatingSystem")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("PlexId")
|
b.Property<string>("ServerName")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.ToTable("PlexMediaFile", (string)null);
|
b.ToTable("EmbyMediaSource", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
||||||
|
|
||||||
|
b.Property<string>("OperatingSystem")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ServerName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinMediaSource", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
||||||
|
|
||||||
|
b.ToTable("LocalMediaSource", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
|
||||||
@@ -2461,35 +2533,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("ProgramScheduleOneItem", (string)null);
|
b.ToTable("ProgramScheduleOneItem", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
|
||||||
|
|
||||||
b.Property<int>("SeasonNumber")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<int>("ShowId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.HasIndex("ShowId");
|
|
||||||
|
|
||||||
b.ToTable("Season", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
|
||||||
|
|
||||||
b.ToTable("Show", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Song", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
|
||||||
|
|
||||||
b.ToTable("Song", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyEpisode", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyEpisode", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Episode");
|
b.HasBaseType("ErsatzTV.Core.Domain.Episode");
|
||||||
@@ -2503,45 +2546,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("EmbyEpisode", (string)null);
|
b.ToTable("EmbyEpisode", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMovie", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Movie");
|
|
||||||
|
|
||||||
b.Property<string>("Etag")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ItemId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("EmbyMovie", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbySeason", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Season");
|
|
||||||
|
|
||||||
b.Property<string>("Etag")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ItemId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("EmbySeason", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyShow", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Show");
|
|
||||||
|
|
||||||
b.Property<string>("Etag")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ItemId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("EmbyShow", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinEpisode", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinEpisode", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Episode");
|
b.HasBaseType("ErsatzTV.Core.Domain.Episode");
|
||||||
@@ -2555,45 +2559,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("JellyfinEpisode", (string)null);
|
b.ToTable("JellyfinEpisode", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMovie", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Movie");
|
|
||||||
|
|
||||||
b.Property<string>("Etag")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ItemId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("JellyfinMovie", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinSeason", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Season");
|
|
||||||
|
|
||||||
b.Property<string>("Etag")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ItemId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("JellyfinSeason", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinShow", b =>
|
|
||||||
{
|
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Show");
|
|
||||||
|
|
||||||
b.Property<string>("Etag")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("ItemId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.ToTable("JellyfinShow", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexEpisode", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexEpisode", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Episode");
|
b.HasBaseType("ErsatzTV.Core.Domain.Episode");
|
||||||
@@ -2607,6 +2572,32 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("PlexEpisode", (string)null);
|
b.ToTable("PlexEpisode", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMovie", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.Movie");
|
||||||
|
|
||||||
|
b.Property<string>("Etag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.ToTable("EmbyMovie", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMovie", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.Movie");
|
||||||
|
|
||||||
|
b.Property<string>("Etag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinMovie", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Movie");
|
b.HasBaseType("ErsatzTV.Core.Domain.Movie");
|
||||||
@@ -2620,6 +2611,32 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("PlexMovie", (string)null);
|
b.ToTable("PlexMovie", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbySeason", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.Season");
|
||||||
|
|
||||||
|
b.Property<string>("Etag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.ToTable("EmbySeason", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinSeason", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.Season");
|
||||||
|
|
||||||
|
b.Property<string>("Etag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinSeason", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexSeason", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexSeason", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Season");
|
b.HasBaseType("ErsatzTV.Core.Domain.Season");
|
||||||
@@ -2633,6 +2650,32 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.ToTable("PlexSeason", (string)null);
|
b.ToTable("PlexSeason", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyShow", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.Show");
|
||||||
|
|
||||||
|
b.Property<string>("Etag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.ToTable("EmbyShow", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinShow", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("ErsatzTV.Core.Domain.Show");
|
||||||
|
|
||||||
|
b.Property<string>("Etag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.ToTable("JellyfinShow", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexShow", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexShow", b =>
|
||||||
{
|
{
|
||||||
b.HasBaseType("ErsatzTV.Core.Domain.Show");
|
b.HasBaseType("ErsatzTV.Core.Domain.Show");
|
||||||
@@ -3371,6 +3414,25 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.Navigation("PlexMediaSource");
|
b.Navigation("PlexMediaSource");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleAlternate", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout")
|
||||||
|
.WithMany("ProgramScheduleAlternates")
|
||||||
|
.HasForeignKey("PlayoutId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule")
|
||||||
|
.WithMany("ProgramScheduleAlternates")
|
||||||
|
.HasForeignKey("ProgramScheduleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Playout");
|
||||||
|
|
||||||
|
b.Navigation("ProgramSchedule");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
|
b.HasOne("ErsatzTV.Core.Domain.Collection", "Collection")
|
||||||
@@ -3675,15 +3737,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Artist", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.Artist", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyLibrary", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
||||||
@@ -3693,11 +3746,47 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMediaSource", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinLibrary", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
||||||
.WithOne()
|
.WithOne()
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.EmbyMediaSource", "Id")
|
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinLibrary", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.MediaFile", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.PlexMediaFile", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Artist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.Artist", "Id")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
@@ -3719,42 +3808,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.Navigation("Season");
|
b.Navigation("Season");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinLibrary", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinLibrary", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMediaSource", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinMediaSource", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalLibrary", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.LocalLibrary", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
||||||
@@ -3790,20 +3843,64 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Library", null)
|
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
||||||
.WithOne()
|
.WithOne()
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.PlexLibrary", "Id")
|
.HasForeignKey("ErsatzTV.Core.Domain.Season", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Show", "Show")
|
||||||
|
.WithMany("Seasons")
|
||||||
|
.HasForeignKey("ShowId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Show");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.Show", "Id")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaFile", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Song", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaFile", null)
|
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
||||||
.WithOne()
|
.WithOne()
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.PlexMediaFile", "Id")
|
.HasForeignKey("ErsatzTV.Core.Domain.Song", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.EmbyMediaSource", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinMediaSource", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
@@ -3853,41 +3950,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.Season", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Show", "Show")
|
|
||||||
.WithMany("Seasons")
|
|
||||||
.HasForeignKey("ShowId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Show");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Show", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.Show", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Song", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.Song", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyEpisode", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyEpisode", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Episode", null)
|
b.HasOne("ErsatzTV.Core.Domain.Episode", null)
|
||||||
@@ -3897,33 +3959,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMovie", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Movie", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.EmbyMovie", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbySeason", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Season", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.EmbySeason", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyShow", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Show", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.EmbyShow", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinEpisode", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinEpisode", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Episode", null)
|
b.HasOne("ErsatzTV.Core.Domain.Episode", null)
|
||||||
@@ -3933,33 +3968,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMovie", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Movie", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinMovie", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinSeason", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Season", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinSeason", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinShow", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Show", null)
|
|
||||||
.WithOne()
|
|
||||||
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinShow", "Id")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexEpisode", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexEpisode", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Episode", null)
|
b.HasOne("ErsatzTV.Core.Domain.Episode", null)
|
||||||
@@ -3969,6 +3977,24 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMovie", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Movie", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.EmbyMovie", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMovie", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Movie", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinMovie", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMovie", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Movie", null)
|
b.HasOne("ErsatzTV.Core.Domain.Movie", null)
|
||||||
@@ -3978,6 +4004,24 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbySeason", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Season", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.EmbySeason", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinSeason", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Season", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinSeason", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexSeason", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexSeason", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Season", null)
|
b.HasOne("ErsatzTV.Core.Domain.Season", null)
|
||||||
@@ -3987,6 +4031,24 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyShow", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Show", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.EmbyShow", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinShow", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ErsatzTV.Core.Domain.Show", null)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("ErsatzTV.Core.Domain.JellyfinShow", "Id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexShow", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexShow", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ErsatzTV.Core.Domain.Show", null)
|
b.HasOne("ErsatzTV.Core.Domain.Show", null)
|
||||||
@@ -4157,6 +4219,8 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
b.Navigation("Items");
|
b.Navigation("Items");
|
||||||
|
|
||||||
|
b.Navigation("ProgramScheduleAlternates");
|
||||||
|
|
||||||
b.Navigation("ProgramScheduleAnchors");
|
b.Navigation("ProgramScheduleAnchors");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4165,6 +4229,8 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.Navigation("Items");
|
b.Navigation("Items");
|
||||||
|
|
||||||
b.Navigation("Playouts");
|
b.Navigation("Playouts");
|
||||||
|
|
||||||
|
b.Navigation("ProgramScheduleAlternates");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.SeasonMetadata", b =>
|
||||||
@@ -4233,42 +4299,28 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.Navigation("Guids");
|
b.Navigation("Guids");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Artist", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("ArtistMetadata");
|
|
||||||
|
|
||||||
b.Navigation("MusicVideos");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyLibrary", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("PathInfos");
|
b.Navigation("PathInfos");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMediaSource", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Connections");
|
|
||||||
|
|
||||||
b.Navigation("PathReplacements");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("EpisodeMetadata");
|
|
||||||
|
|
||||||
b.Navigation("MediaVersions");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinLibrary", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinLibrary", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("PathInfos");
|
b.Navigation("PathInfos");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMediaSource", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Artist", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Connections");
|
b.Navigation("ArtistMetadata");
|
||||||
|
|
||||||
b.Navigation("PathReplacements");
|
b.Navigation("MusicVideos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Episode", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("EpisodeMetadata");
|
||||||
|
|
||||||
|
b.Navigation("MediaVersions");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Movie", b =>
|
||||||
@@ -4292,13 +4344,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
b.Navigation("OtherVideoMetadata");
|
b.Navigation("OtherVideoMetadata");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Connections");
|
|
||||||
|
|
||||||
b.Navigation("PathReplacements");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Season", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Episodes");
|
b.Navigation("Episodes");
|
||||||
@@ -4319,6 +4364,27 @@ namespace ErsatzTV.Infrastructure.Migrations
|
|||||||
|
|
||||||
b.Navigation("SongMetadata");
|
b.Navigation("SongMetadata");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.EmbyMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Connections");
|
||||||
|
|
||||||
|
b.Navigation("PathReplacements");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.JellyfinMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Connections");
|
||||||
|
|
||||||
|
b.Navigation("PathReplacements");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Connections");
|
||||||
|
|
||||||
|
b.Navigation("PathReplacements");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,486 @@
|
|||||||
|
@page "/playouts/{Id:int}/alternate-schedules"
|
||||||
|
@using ErsatzTV.Application.ProgramSchedules
|
||||||
|
@using ErsatzTV.Application.Playouts
|
||||||
|
@using Microsoft.AspNetCore.Components
|
||||||
|
@using ErsatzTV.Application.Channels
|
||||||
|
@using System.Text
|
||||||
|
@using System.Globalization
|
||||||
|
@implements IDisposable
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject ILogger<ScheduleItemsEditor> Logger
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
@inject IMediator Mediator
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
|
||||||
|
<MudTable Hover="true" Items="_items.OrderBy(i => i.Index)" Dense="true" @bind-SelectedItem="_selectedItem">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudText Typo="Typo.h6">@_channelName Alternate Schedules</MudText>
|
||||||
|
<MudSpacer />
|
||||||
|
<MudText Typo="Typo.subtitle1" Class="mr-3">In priority order from top to bottom</MudText>
|
||||||
|
</ToolBarContent>
|
||||||
|
<ColGroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col style="width: 60px;"/>
|
||||||
|
<col style="width: 60px;"/>
|
||||||
|
<col style="width: 60px;"/>
|
||||||
|
</ColGroup>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>Schedule</MudTh>
|
||||||
|
<MudTh>Days of the Week</MudTh>
|
||||||
|
<MudTh>Days of the Month</MudTh>
|
||||||
|
<MudTh>Months</MudTh>
|
||||||
|
<MudTh/>
|
||||||
|
<MudTh/>
|
||||||
|
<MudTh/>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd DataLabel="Schedule">
|
||||||
|
<MudText Typo="@(context == _selectedItem ? Typo.subtitle2 : Typo.body2)">
|
||||||
|
@context.ProgramSchedule.Name
|
||||||
|
</MudText>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Days of the Week">
|
||||||
|
<MudText Typo="@(context == _selectedItem ? Typo.subtitle2 : Typo.body2)">
|
||||||
|
@ToDaysOfWeekString(context.DaysOfWeek)
|
||||||
|
</MudText>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Days of the Month">
|
||||||
|
<MudText Typo="@(context == _selectedItem ? Typo.subtitle2 : Typo.body2)">
|
||||||
|
@ToDaysOfMonthString(context.DaysOfMonth)
|
||||||
|
</MudText>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Months">
|
||||||
|
<MudText Typo="@(context == _selectedItem ? Typo.subtitle2 : Typo.body2)">
|
||||||
|
@ToMonthsOfYearString(context.MonthsOfYear)
|
||||||
|
</MudText>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.ArrowUpward"
|
||||||
|
OnClick="@(_ => MoveItemUp(context))"
|
||||||
|
Disabled="@(_items.All(x => x.Index >= context.Index))">
|
||||||
|
</MudIconButton>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.ArrowDownward"
|
||||||
|
OnClick="@(_ => MoveItemDown(context))"
|
||||||
|
Disabled="@(_items.All(x => x.Index <= context.Index))">
|
||||||
|
</MudIconButton>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||||
|
OnClick="@(_ => RemoveAlternateSchedule(context))"
|
||||||
|
Disabled="@(_items.Count == 1)">
|
||||||
|
</MudIconButton>
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Default" OnClick="@(_ => AddAlternateSchedule())" Class="mt-4">
|
||||||
|
Add Alternate Schedule
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => SaveChanges())" Class="mt-4 ml-4">
|
||||||
|
Save Changes
|
||||||
|
</MudButton>
|
||||||
|
|
||||||
|
@if (_selectedItem is not null)
|
||||||
|
{
|
||||||
|
<EditForm Model="_selectedItem">
|
||||||
|
<FluentValidator/>
|
||||||
|
<div style="display: flex; flex-direction: row;" class="mt-6">
|
||||||
|
<div style="flex-grow: 1; max-width: 400px;" class="mr-6">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudSelect Label="Schedule" @bind-Value="_selectedItem.ProgramSchedule" For="@(() => _selectedItem.ProgramSchedule)">
|
||||||
|
@foreach (ProgramScheduleViewModel schedule in _schedules)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@schedule">@schedule.Name</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
<MudCard Class="mt-4">
|
||||||
|
<MudCardContent>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-3">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetDayName(DayOfWeek.Monday)"
|
||||||
|
Checked="@(_selectedItem.DaysOfWeek.Contains(DayOfWeek.Monday))"
|
||||||
|
CheckedChanged="@(c => DayOfWeekChanged(DayOfWeek.Monday, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-2">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetDayName(DayOfWeek.Tuesday)"
|
||||||
|
Checked="@(_selectedItem.DaysOfWeek.Contains(DayOfWeek.Tuesday))"
|
||||||
|
CheckedChanged="@(c => DayOfWeekChanged(DayOfWeek.Tuesday, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-2">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetDayName(DayOfWeek.Wednesday)"
|
||||||
|
Checked="@(_selectedItem.DaysOfWeek.Contains(DayOfWeek.Wednesday))"
|
||||||
|
CheckedChanged="@(c => DayOfWeekChanged(DayOfWeek.Wednesday, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-2">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetDayName(DayOfWeek.Thursday)"
|
||||||
|
Checked="@(_selectedItem.DaysOfWeek.Contains(DayOfWeek.Thursday))"
|
||||||
|
CheckedChanged="@(c => DayOfWeekChanged(DayOfWeek.Thursday, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-2">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetDayName(DayOfWeek.Friday)"
|
||||||
|
Checked="@(_selectedItem.DaysOfWeek.Contains(DayOfWeek.Friday))"
|
||||||
|
CheckedChanged="@(c => DayOfWeekChanged(DayOfWeek.Friday, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-2">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetDayName(DayOfWeek.Saturday)"
|
||||||
|
Checked="@(_selectedItem.DaysOfWeek.Contains(DayOfWeek.Saturday))"
|
||||||
|
CheckedChanged="@(c => DayOfWeekChanged(DayOfWeek.Saturday, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-2">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetDayName(DayOfWeek.Sunday)"
|
||||||
|
Checked="@(_selectedItem.DaysOfWeek.Contains(DayOfWeek.Sunday))"
|
||||||
|
CheckedChanged="@(c => DayOfWeekChanged(DayOfWeek.Sunday, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectWeekdays())">
|
||||||
|
Weekdays
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectWeekends())">
|
||||||
|
Weekends
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectAllDaysOfWeek())">
|
||||||
|
All
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectNoDaysOfWeek())">
|
||||||
|
None
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</div>
|
||||||
|
<div style="flex-grow: 1; max-width: 400px;" class="mr-6">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudGrid Justify="Justify.FlexStart" Class="mt-3">
|
||||||
|
@foreach (int day in Enumerable.Range(1, 31))
|
||||||
|
{
|
||||||
|
<MudItem xs="3">
|
||||||
|
<MudCheckBox T="bool" Label="@day.ToString()"
|
||||||
|
Checked="@(_selectedItem.DaysOfMonth.Contains(day))"
|
||||||
|
CheckedChanged="@(c => DayOfMonthChanged(day, c))"/>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
</MudGrid>
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectAllDaysOfMonth())">
|
||||||
|
All
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectNoDaysOfMonth())">
|
||||||
|
None
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</div>
|
||||||
|
<div style="flex-grow: 1; max-width: 400px;">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-3">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetMonthName(1)"
|
||||||
|
Checked="@(_selectedItem.MonthsOfYear.Contains(1))"
|
||||||
|
CheckedChanged="@(c => MonthOfYearChanged(1, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
@foreach (int month in Enumerable.Range(2, 11))
|
||||||
|
{
|
||||||
|
<MudElement HtmlTag="div" Class="mt-2">
|
||||||
|
<MudCheckBox T="bool" Label="@_dtf.GetMonthName(month)"
|
||||||
|
Checked="@(_selectedItem.MonthsOfYear.Contains(month))"
|
||||||
|
CheckedChanged="@(c => MonthOfYearChanged(month, c))"/>
|
||||||
|
</MudElement>
|
||||||
|
}
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectAllMonthsOfYear())">
|
||||||
|
All
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="@(_ => SelectNoMonthsOfYear())">
|
||||||
|
None
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
}
|
||||||
|
</MudContainer>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private readonly CancellationTokenSource _cts = new();
|
||||||
|
|
||||||
|
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
private string _channelName;
|
||||||
|
private List<PlayoutAlternateScheduleEditViewModel> _items;
|
||||||
|
private List<ProgramScheduleViewModel> _schedules;
|
||||||
|
|
||||||
|
private PlayoutAlternateScheduleEditViewModel _selectedItem;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_cts.Cancel();
|
||||||
|
_cts.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync() => await LoadScheduleItems();
|
||||||
|
|
||||||
|
private async Task LoadScheduleItems()
|
||||||
|
{
|
||||||
|
_schedules = await Mediator.Send(new GetAllProgramSchedules(), _cts.Token)
|
||||||
|
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
|
||||||
|
|
||||||
|
_channelName = (await Mediator.Send(new GetChannelNameByPlayoutId(Id), _cts.Token)).IfNone(string.Empty);
|
||||||
|
|
||||||
|
List<PlayoutAlternateScheduleViewModel> results = await Mediator.Send(new GetPlayoutAlternateSchedules(Id), _cts.Token);
|
||||||
|
_items = results.Map(ProjectToEditViewModel).ToList();
|
||||||
|
if (_items.Count == 1)
|
||||||
|
{
|
||||||
|
_selectedItem = _items.Head();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayoutAlternateScheduleEditViewModel ProjectToEditViewModel(PlayoutAlternateScheduleViewModel item) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
Index = item.Index,
|
||||||
|
ProgramSchedule = _schedules.Find(vm => vm.Id == item.ProgramScheduleId),
|
||||||
|
DaysOfWeek = item.DaysOfWeek.OrderBy(x => ((int)x + 6) % 7).ToList(),
|
||||||
|
DaysOfMonth = item.DaysOfMonth.ToList(),
|
||||||
|
MonthsOfYear = item.MonthsOfYear.ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
private void DayOfWeekChanged(DayOfWeek dayOfWeek, bool isChecked)
|
||||||
|
{
|
||||||
|
if (isChecked && !_selectedItem.DaysOfWeek.Contains(dayOfWeek))
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfWeek.Add(dayOfWeek);
|
||||||
|
_selectedItem.DaysOfWeek = _selectedItem.DaysOfWeek.OrderBy(x => ((int)x + 6) % 7).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isChecked)
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfWeek.Remove(dayOfWeek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectWeekdays()
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfWeek.Clear();
|
||||||
|
_selectedItem.DaysOfWeek.AddRange(new[]
|
||||||
|
{
|
||||||
|
DayOfWeek.Monday,
|
||||||
|
DayOfWeek.Tuesday,
|
||||||
|
DayOfWeek.Wednesday,
|
||||||
|
DayOfWeek.Thursday,
|
||||||
|
DayOfWeek.Friday
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectWeekends()
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfWeek.Clear();
|
||||||
|
_selectedItem.DaysOfWeek.AddRange(new[]
|
||||||
|
{
|
||||||
|
DayOfWeek.Saturday,
|
||||||
|
DayOfWeek.Sunday
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectAllDaysOfWeek()
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfWeek.Clear();
|
||||||
|
_selectedItem.DaysOfWeek.AddRange(ProgramScheduleAlternate.AllDaysOfWeek());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectNoDaysOfWeek()
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfWeek.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DayOfMonthChanged(int dayOfMonth, bool isChecked)
|
||||||
|
{
|
||||||
|
if (isChecked && !_selectedItem.DaysOfMonth.Contains(dayOfMonth))
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfMonth.Add(dayOfMonth);
|
||||||
|
_selectedItem.DaysOfMonth.Sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isChecked)
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfMonth.Remove(dayOfMonth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectAllDaysOfMonth()
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfMonth.Clear();
|
||||||
|
_selectedItem.DaysOfMonth.AddRange(ProgramScheduleAlternate.AllDaysOfMonth());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectNoDaysOfMonth()
|
||||||
|
{
|
||||||
|
_selectedItem.DaysOfMonth.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MonthOfYearChanged(int monthOfYear, bool isChecked)
|
||||||
|
{
|
||||||
|
if (isChecked && !_selectedItem.MonthsOfYear.Contains(monthOfYear))
|
||||||
|
{
|
||||||
|
_selectedItem.MonthsOfYear.Add(monthOfYear);
|
||||||
|
_selectedItem.MonthsOfYear.Sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isChecked)
|
||||||
|
{
|
||||||
|
_selectedItem.MonthsOfYear.Remove(monthOfYear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectAllMonthsOfYear()
|
||||||
|
{
|
||||||
|
_selectedItem.MonthsOfYear.Clear();
|
||||||
|
_selectedItem.MonthsOfYear.AddRange(ProgramScheduleAlternate.AllMonthsOfYear());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectNoMonthsOfYear()
|
||||||
|
{
|
||||||
|
_selectedItem.MonthsOfYear.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAlternateSchedule()
|
||||||
|
{
|
||||||
|
var item = new PlayoutAlternateScheduleEditViewModel
|
||||||
|
{
|
||||||
|
Index = _items.Map(i => i.Index).DefaultIfEmpty().Max() + 1,
|
||||||
|
ProgramSchedule = _schedules.Head(),
|
||||||
|
DaysOfWeek = ProgramScheduleAlternate.AllDaysOfWeek(),
|
||||||
|
DaysOfMonth = ProgramScheduleAlternate.AllDaysOfMonth(),
|
||||||
|
MonthsOfYear = ProgramScheduleAlternate.AllMonthsOfYear()
|
||||||
|
};
|
||||||
|
|
||||||
|
_items.Add(item);
|
||||||
|
_selectedItem = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveAlternateSchedule(PlayoutAlternateScheduleEditViewModel item)
|
||||||
|
{
|
||||||
|
_selectedItem = null;
|
||||||
|
_items.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MoveItemUp(PlayoutAlternateScheduleEditViewModel item)
|
||||||
|
{
|
||||||
|
// swap with lower index
|
||||||
|
PlayoutAlternateScheduleEditViewModel toSwap = _items.OrderByDescending(x => x.Index).First(x => x.Index < item.Index);
|
||||||
|
(toSwap.Index, item.Index) = (item.Index, toSwap.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MoveItemDown(PlayoutAlternateScheduleEditViewModel item)
|
||||||
|
{
|
||||||
|
// swap with higher index
|
||||||
|
PlayoutAlternateScheduleEditViewModel toSwap = _items.OrderBy(x => x.Index).First(x => x.Index > item.Index);
|
||||||
|
(toSwap.Index, item.Index) = (item.Index, toSwap.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveChanges()
|
||||||
|
{
|
||||||
|
var items = _items.Map(item => new ReplacePlayoutAlternateSchedule(
|
||||||
|
item.Id,
|
||||||
|
item.Index,
|
||||||
|
item.ProgramSchedule.Id,
|
||||||
|
item.DaysOfWeek,
|
||||||
|
item.DaysOfMonth,
|
||||||
|
item.MonthsOfYear)).ToList();
|
||||||
|
|
||||||
|
Seq<BaseError> errorMessages = await Mediator.Send(new ReplacePlayoutAlternateScheduleItems(Id, items), _cts.Token)
|
||||||
|
.Map(e => e.LeftToSeq());
|
||||||
|
|
||||||
|
errorMessages.HeadOrNone().Match(
|
||||||
|
error =>
|
||||||
|
{
|
||||||
|
Snackbar.Add($"Unexpected error saving alternate schedules: {error.Value}", Severity.Error);
|
||||||
|
Logger.LogError("Unexpected error saving alternate schedules: {Error}", error.Value);
|
||||||
|
},
|
||||||
|
() => NavigationManager.NavigateTo("/playouts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ToDaysOfWeekString(List<DayOfWeek> daysOfWeek)
|
||||||
|
{
|
||||||
|
if (daysOfWeek.Count is 0 or 7)
|
||||||
|
{
|
||||||
|
return "*any*";
|
||||||
|
}
|
||||||
|
|
||||||
|
daysOfWeek.Sort();
|
||||||
|
|
||||||
|
return string.Join(", ", daysOfWeek.Map(_dtf.GetAbbreviatedDayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ToDaysOfMonthString(List<int> daysOfMonth)
|
||||||
|
{
|
||||||
|
if (daysOfMonth.Count is 0 or 31)
|
||||||
|
{
|
||||||
|
return "*any*";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToRangeString(daysOfMonth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ToMonthsOfYearString(List<int> monthsOfYear)
|
||||||
|
{
|
||||||
|
if (monthsOfYear.Count is 0 or 12)
|
||||||
|
{
|
||||||
|
return "*any*";
|
||||||
|
}
|
||||||
|
|
||||||
|
monthsOfYear.Sort();
|
||||||
|
|
||||||
|
return string.Join(", ", monthsOfYear.Map(_dtf.GetAbbreviatedMonthName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToRangeString(List<int> list)
|
||||||
|
{
|
||||||
|
list = list.Distinct().ToList();
|
||||||
|
list.Sort();
|
||||||
|
|
||||||
|
var result = new StringBuilder();
|
||||||
|
for (var i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
int temp = list[i];
|
||||||
|
|
||||||
|
//add a number
|
||||||
|
result.Append(list[i]);
|
||||||
|
|
||||||
|
//skip number(s) between a range
|
||||||
|
while (i < list.Count - 1 && list[i + 1] == list[i] + 1)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//add the range
|
||||||
|
if (temp != list[i])
|
||||||
|
{
|
||||||
|
result.Append("-").Append(list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//add comma
|
||||||
|
if (i != list.Count - 1)
|
||||||
|
{
|
||||||
|
result.Append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
</MudTh>
|
</MudTh>
|
||||||
<MudTh>
|
<MudTh>
|
||||||
<MudTableSortLabel SortBy="new Func<PlayoutViewModel, object>(x => x.ProgramSchedule.Name)">
|
<MudTableSortLabel SortBy="new Func<PlayoutViewModel, object>(x => x.ProgramSchedule.Name)">
|
||||||
Schedule
|
Default Schedule
|
||||||
</MudTableSortLabel>
|
</MudTableSortLabel>
|
||||||
</MudTh>
|
</MudTh>
|
||||||
@* <MudTh>Playout Type</MudTh> *@
|
@* <MudTh>Playout Type</MudTh> *@
|
||||||
@@ -45,6 +45,11 @@
|
|||||||
@* <MudTd DataLabel="Playout Type">@context.ProgramSchedulePlayoutType</MudTd> *@
|
@* <MudTd DataLabel="Playout Type">@context.ProgramSchedulePlayoutType</MudTd> *@
|
||||||
<MudTd>
|
<MudTd>
|
||||||
<div style="align-items: center; display: flex;">
|
<div style="align-items: center; display: flex;">
|
||||||
|
<MudTooltip Text="Edit Alternate Schedules">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.EditCalendar"
|
||||||
|
Link="@($"playouts/{context.PlayoutId}/alternate-schedules")">
|
||||||
|
</MudIconButton>
|
||||||
|
</MudTooltip>
|
||||||
<MudTooltip Text="Reset Playout">
|
<MudTooltip Text="Reset Playout">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
|
||||||
OnClick="@(_ => ResetPlayout(context))">
|
OnClick="@(_ => ResetPlayout(context))">
|
||||||
@@ -168,7 +173,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ResetPlayout(PlayoutNameViewModel playout)
|
private async Task ResetPlayout(PlayoutNameViewModel playout)
|
||||||
{
|
{
|
||||||
await _mediator.Send(new BuildPlayout(playout.PlayoutId, PlayoutBuildMode.Reset), _cts.Token);
|
await _mediator.Send(new BuildPlayout(playout.PlayoutId, PlayoutBuildMode.Reset), _cts.Token);
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using ErsatzTV.ViewModels;
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Validators;
|
||||||
|
|
||||||
|
public class PlayoutAlternateScheduleEditViewModelValidator : AbstractValidator<PlayoutAlternateScheduleEditViewModel>
|
||||||
|
{
|
||||||
|
public PlayoutAlternateScheduleEditViewModelValidator()
|
||||||
|
{
|
||||||
|
RuleFor(p => p.ProgramSchedule).NotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using ErsatzTV.Application.ProgramSchedules;
|
||||||
|
|
||||||
|
namespace ErsatzTV.ViewModels;
|
||||||
|
|
||||||
|
public class PlayoutAlternateScheduleEditViewModel
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int Index { get; set; }
|
||||||
|
public ProgramScheduleViewModel ProgramSchedule { get; set; }
|
||||||
|
public List<DayOfWeek> DaysOfWeek { get; set; }
|
||||||
|
public List<int> DaysOfMonth { get; set; }
|
||||||
|
public List<int> MonthsOfYear { get; set; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user