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

419 lines
22 KiB

@page "/decos/{Id:int}"
@using ErsatzTV.Application.Artists
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaItems
@using ErsatzTV.Application.Scheduling
@using ErsatzTV.Application.Television
@using ErsatzTV.Application.Watermarks
@using ErsatzTV.Core.Domain.Scheduling
@implements IDisposable
@inject NavigationManager NavigationManager
@inject ILogger<DecoEditor> Logger
@inject ISnackbar Snackbar
@inject IMediator Mediator
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8" Style="display: flex; flex-direction: row">
<MudStack Class="mr-6">
<MudText Typo="Typo.h4" Class="mb-4">Edit Deco</MudText>
<MudCard Class="mb-6" Style="width: 350px">
<MudCardContent>
<MudTextField Label="Name" @bind-Value="_deco.Name" For="@(() => _deco.Name)"/>
</MudCardContent>
<MudCardActions>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => SaveChanges())" StartIcon="@Icons.Material.Filled.Save">
Save Changes
</MudButton>
</MudCardActions>
</MudCard>
<MudCard Class="mb-6" Style="width: 350px">
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.h6">Watermark</MudText>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent>
<MudSelect Label="Watermark Mode" @bind-Value="_deco.WatermarkMode" For="@(() => _deco.WatermarkMode)">
<MudSelectItem Value="DecoMode.Inherit">Inherit</MudSelectItem>
<MudSelectItem Value="DecoMode.Disable">Disable</MudSelectItem>
<MudSelectItem Value="DecoMode.Override">Override</MudSelectItem>
</MudSelect>
<MudSelect Disabled="@(_deco.WatermarkMode != DecoMode.Override)" Label="Watermark Override" @bind-Value="_deco.WatermarkId" For="@(() => _deco.WatermarkId)"
Clearable="true">
<MudSelectItem T="int?" Value="@((int?)null)">(none)</MudSelectItem>
@foreach (WatermarkViewModel watermark in _watermarks)
{
<MudSelectItem T="int?" Value="@watermark.Id">@watermark.Name</MudSelectItem>
}
</MudSelect>
</MudCardContent>
</MudCard>
<MudCard Class="mb-6" Style="width: 350px">
<MudCardHeader>
<CardHeaderContent>
<MudTooltip Style="max-width: 350px">
<ChildContent>
<MudText Typo="Typo.h6" Class="d-flex align-center justify-center">
Default Filler
&nbsp;
<MudIcon Icon="@Icons.Material.Filled.Info"/>
</MudText>
</ChildContent>
<TooltipContent>
<MudText Typo="Typo.body2">After all blocks have been scheduled, a second pass will be made to fill unscheduled time using random items from this collection.</MudText>
</TooltipContent>
</MudTooltip>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent>
<MudSelect Label="Default Filler Mode" @bind-Value="_deco.DefaultFillerMode" For="@(() => _deco.DefaultFillerMode)">
<MudSelectItem Value="DecoMode.Inherit">Inherit</MudSelectItem>
<MudSelectItem Value="DecoMode.Disable">Disable</MudSelectItem>
<MudSelectItem Value="DecoMode.Override">Override</MudSelectItem>
</MudSelect>
<MudSelect Disabled="@(_deco.DefaultFillerMode != DecoMode.Override)"
Label="Default Filler Collection Type"
@bind-Value="_deco.DefaultFillerCollectionType"
For="@(() => _deco.DefaultFillerCollectionType)">
<MudSelectItem Value="ProgramScheduleItemCollectionType.Collection">Collection</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionShow">Television Show</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionSeason">Television Season</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.Artist">Artist</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.MultiCollection">Multi Collection</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.SmartCollection">Smart Collection</MudSelectItem>
</MudSelect>
@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.Collection)
{
<MudSelect Class="mt-3"
T="MediaCollectionViewModel"
Disabled="@(_deco.DefaultFillerMode != DecoMode.Override)"
Label="Collection"
@bind-value="_deco.DefaultFillerCollection">
@foreach (MediaCollectionViewModel collection in _mediaCollections)
{
<MudSelectItem Value="@collection">@collection.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.MultiCollection)
{
<MudSelect Class="mt-3"
T="MultiCollectionViewModel"
Disabled="@(_deco.DefaultFillerMode != DecoMode.Override)"
Label="Multi Collection"
@bind-value="_deco.DefaultFillerMultiCollection">
@foreach (MultiCollectionViewModel collection in _multiCollections)
{
<MudSelectItem Value="@collection">@collection.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.SmartCollection)
{
<MudSelect Class="mt-3"
T="SmartCollectionViewModel"
Disabled="@(_deco.DefaultFillerMode != DecoMode.Override)"
Label="Smart Collection"
@bind-value="_deco.DefaultFillerSmartCollection">
@foreach (SmartCollectionViewModel collection in _smartCollections)
{
<MudSelectItem Value="@collection">@collection.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.TelevisionShow)
{
<MudSelect Class="mt-3"
T="NamedMediaItemViewModel"
Disabled="@(_deco.DefaultFillerMode != DecoMode.Override)"
Label="Television Show"
@bind-value="_deco.DefaultFillerMediaItem">
@foreach (NamedMediaItemViewModel show in _televisionShows)
{
<MudSelectItem Value="@show">@show.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.TelevisionSeason)
{
<MudSelect Class="mt-3"
T="NamedMediaItemViewModel"
Disabled="@(_deco.DefaultFillerMode != DecoMode.Override)"
Label="Television Season"
@bind-value="_deco.DefaultFillerMediaItem">
@foreach (NamedMediaItemViewModel season in _televisionSeasons)
{
<MudSelectItem Value="@season">@season.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.Artist)
{
<MudSelect Class="mt-3"
T="NamedMediaItemViewModel"
Disabled="@(_deco.DefaultFillerMode != DecoMode.Override)"
Label="Artist"
@bind-value="_deco.DefaultFillerMediaItem">
@foreach (NamedMediaItemViewModel artist in _artists)
{
<MudSelectItem Value="@artist">@artist.Name</MudSelectItem>
}
</MudSelect>
}
<MudSwitch T="bool" Class="mt-3" Label="Trim To Fit" @bind-Value="_deco.DefaultFillerTrimToFit" Color="Color.Primary" />
</MudCardContent>
</MudCard>
<MudCard Class="mb-6" Style="width: 350px">
<MudCardHeader>
<CardHeaderContent>
<MudTooltip Style="max-width: 350px">
<ChildContent>
<MudText Typo="Typo.h6" Class="d-flex align-center justify-center">
Dead Air Fallback
&nbsp;
<MudIcon Icon="@Icons.Material.Filled.Info"/>
</MudText>
</ChildContent>
<TooltipContent>
<MudText Typo="Typo.body2">When no playout item is found for the current time, *one* item will be randomly selected from this collection and looped and trimmed to exactly fit until the start of the next playout item.</MudText>
<MudText Typo="Typo.body2" Class="mt-3">This replaces the "Channel is Offline" image that would otherwise display.</MudText>
</TooltipContent>
</MudTooltip>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent>
<MudSelect Label="Dead Air Fallback Mode" @bind-Value="_deco.DeadAirFallbackMode" For="@(() => _deco.DeadAirFallbackMode)">
<MudSelectItem Value="DecoMode.Inherit">Inherit</MudSelectItem>
<MudSelectItem Value="DecoMode.Disable">Disable</MudSelectItem>
<MudSelectItem Value="DecoMode.Override">Override</MudSelectItem>
</MudSelect>
<MudSelect Disabled="@(_deco.DeadAirFallbackMode != DecoMode.Override)"
Label="Dead Air Fallback Collection Type"
@bind-Value="_deco.DeadAirFallbackCollectionType"
For="@(() => _deco.DeadAirFallbackCollectionType)">
<MudSelectItem Value="ProgramScheduleItemCollectionType.Collection">Collection</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionShow">Television Show</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionSeason">Television Season</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.Artist">Artist</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.MultiCollection">Multi Collection</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.SmartCollection">Smart Collection</MudSelectItem>
</MudSelect>
@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.Collection)
{
<MudSelect Class="mt-3"
T="MediaCollectionViewModel"
Disabled="@(_deco.DeadAirFallbackMode != DecoMode.Override)"
Label="Collection"
@bind-value="_deco.DeadAirFallbackCollection">
@foreach (MediaCollectionViewModel collection in _mediaCollections)
{
<MudSelectItem Value="@collection">@collection.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.MultiCollection)
{
<MudSelect Class="mt-3"
T="MultiCollectionViewModel"
Disabled="@(_deco.DeadAirFallbackMode != DecoMode.Override)"
Label="Multi Collection"
@bind-value="_deco.DeadAirFallbackMultiCollection">
@foreach (MultiCollectionViewModel collection in _multiCollections)
{
<MudSelectItem Value="@collection">@collection.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.SmartCollection)
{
<MudSelect Class="mt-3"
T="SmartCollectionViewModel"
Disabled="@(_deco.DeadAirFallbackMode != DecoMode.Override)"
Label="Smart Collection"
@bind-value="_deco.DeadAirFallbackSmartCollection">
@foreach (SmartCollectionViewModel collection in _smartCollections)
{
<MudSelectItem Value="@collection">@collection.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.TelevisionShow)
{
<MudSelect Class="mt-3"
T="NamedMediaItemViewModel"
Disabled="@(_deco.DeadAirFallbackMode != DecoMode.Override)"
Label="Television Show"
@bind-value="_deco.DeadAirFallbackMediaItem">
@foreach (NamedMediaItemViewModel show in _televisionShows)
{
<MudSelectItem Value="@show">@show.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.TelevisionSeason)
{
<MudSelect Class="mt-3"
T="NamedMediaItemViewModel"
Disabled="@(_deco.DeadAirFallbackMode != DecoMode.Override)"
Label="Television Season"
@bind-value="_deco.DeadAirFallbackMediaItem">
@foreach (NamedMediaItemViewModel season in _televisionSeasons)
{
<MudSelectItem Value="@season">@season.Name</MudSelectItem>
}
</MudSelect>
}
@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.Artist)
{
<MudSelect Class="mt-3"
T="NamedMediaItemViewModel"
Disabled="@(_deco.DeadAirFallbackMode != DecoMode.Override)"
Label="Artist"
@bind-value="_deco.DeadAirFallbackMediaItem">
@foreach (NamedMediaItemViewModel artist in _artists)
{
<MudSelectItem Value="@artist">@artist.Name</MudSelectItem>
}
</MudSelect>
}
</MudCardContent>
</MudCard>
</MudStack>
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
private DecoEditViewModel _deco = new();
private List<WatermarkViewModel> _watermarks = [];
private List<MediaCollectionViewModel> _mediaCollections = [];
private List<MultiCollectionViewModel> _multiCollections = [];
private List<SmartCollectionViewModel> _smartCollections = [];
private List<NamedMediaItemViewModel> _televisionShows = [];
private List<NamedMediaItemViewModel> _televisionSeasons = [];
private List<NamedMediaItemViewModel> _artists = [];
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
await LoadWatermarks();
_mediaCollections = await Mediator.Send(new GetAllCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_multiCollections = await Mediator.Send(new GetAllMultiCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_smartCollections = await Mediator.Send(new GetAllSmartCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_televisionShows = await Mediator.Send(new GetAllTelevisionShows(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_televisionSeasons = await Mediator.Send(new GetAllTelevisionSeasons(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_artists = await Mediator.Send(new GetAllArtists(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
await LoadDeco();
}
private async Task LoadWatermarks() =>
_watermarks = await Mediator.Send(new GetAllWatermarks(), _cts.Token);
private async Task LoadDeco()
{
Option<DecoViewModel> maybeDeco = await Mediator.Send(new GetDecoById(Id), _cts.Token);
if (maybeDeco.IsNone)
{
NavigationManager.NavigateTo("decos");
return;
}
foreach (DecoViewModel deco in maybeDeco)
{
_deco = new DecoEditViewModel
{
Name = deco.Name,
DecoGroupId = deco.DecoGroupId,
WatermarkMode = deco.WatermarkMode,
WatermarkId = deco.WatermarkId,
DefaultFillerMode = deco.DefaultFillerMode,
DefaultFillerCollectionType = deco.DefaultFillerCollectionType,
DefaultFillerCollection = deco.DefaultFillerCollectionId.HasValue
? _mediaCollections.Find(c => c.Id == deco.DefaultFillerCollectionId.Value)
: null,
DefaultFillerMediaItem = deco.DefaultFillerMediaItemId.HasValue
? _televisionShows.Append(_televisionSeasons).Append(_artists).ToList()
.Find(vm => vm.MediaItemId == deco.DefaultFillerMediaItemId.Value)
: null,
DefaultFillerMultiCollection = deco.DefaultFillerMultiCollectionId.HasValue
? _multiCollections.Find(c => c.Id == deco.DefaultFillerMultiCollectionId.Value)
: null,
DefaultFillerSmartCollection = deco.DefaultFillerSmartCollectionId.HasValue
? _smartCollections.Find(c => c.Id == deco.DefaultFillerSmartCollectionId.Value)
: null,
DefaultFillerTrimToFit = deco.DefaultFillerTrimToFit,
DeadAirFallbackMode = deco.DeadAirFallbackMode,
DeadAirFallbackCollectionType = deco.DeadAirFallbackCollectionType,
DeadAirFallbackCollection = deco.DeadAirFallbackCollectionId.HasValue
? _mediaCollections.Find(c => c.Id == deco.DeadAirFallbackCollectionId.Value)
: null,
DeadAirFallbackMediaItem = deco.DeadAirFallbackMediaItemId.HasValue
? _televisionShows.Append(_televisionSeasons).Append(_artists).ToList()
.Find(vm => vm.MediaItemId == deco.DeadAirFallbackMediaItemId.Value)
: null,
DeadAirFallbackMultiCollection = deco.DeadAirFallbackMultiCollectionId.HasValue
? _multiCollections.Find(c => c.Id == deco.DeadAirFallbackMultiCollectionId.Value)
: null,
DeadAirFallbackSmartCollection = deco.DeadAirFallbackSmartCollectionId.HasValue
? _smartCollections.Find(c => c.Id == deco.DeadAirFallbackSmartCollectionId.Value)
: null
};
}
}
private async Task SaveChanges()
{
var request = new UpdateDeco(
Id,
_deco.DecoGroupId,
_deco.Name,
_deco.WatermarkMode,
_deco.WatermarkId,
_deco.DefaultFillerMode,
_deco.DefaultFillerCollectionType,
_deco.DefaultFillerCollection?.Id,
_deco.DefaultFillerMediaItem?.MediaItemId,
_deco.DefaultFillerMultiCollection?.Id,
_deco.DefaultFillerSmartCollection?.Id,
_deco.DefaultFillerTrimToFit,
_deco.DeadAirFallbackMode,
_deco.DeadAirFallbackCollectionType,
_deco.DeadAirFallbackCollection?.Id,
_deco.DeadAirFallbackMediaItem?.MediaItemId,
_deco.DeadAirFallbackMultiCollection?.Id,
_deco.DeadAirFallbackSmartCollection?.Id);
Seq<BaseError> errorMessages = await Mediator
.Send(request, _cts.Token)
.Map(e => e.LeftToSeq());
errorMessages.HeadOrNone().Match(
error =>
{
Snackbar.Add($"Unexpected error saving deco: {error.Value}", Severity.Error);
Logger.LogError("Unexpected error saving deco: {Error}", error.Value);
},
() => NavigationManager.NavigateTo("/decos"));
}
}