Browse Source

add libraries ui

pull/31/head
Jason Dove 6 years ago
parent
commit
b383abf15b
  1. 4
      ErsatzTV.Application/Libraries/LocalLibraryPathViewModel.cs
  2. 4
      ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs
  3. 13
      ErsatzTV.Application/Libraries/Mapper.cs
  4. 7
      ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs
  5. 22
      ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs
  6. 7
      ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPaths.cs
  7. 25
      ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPathsHandler.cs
  8. 2
      ErsatzTV.Application/MediaCollections/Commands/CreateCollectionHandler.cs
  9. 2
      ErsatzTV.CommandLine/Commands/Schedules/ScheduleAddItemCommand.cs
  10. 5
      ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs
  11. 20
      ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs
  12. 1
      ErsatzTV.Infrastructure/Data/TvContext.cs
  13. 8
      ErsatzTV/Pages/CollectionEditor.razor
  14. 2
      ErsatzTV/Pages/CollectionItems.razor
  15. 6
      ErsatzTV/Pages/Collections.razor
  16. 6
      ErsatzTV/Pages/Index.razor
  17. 82
      ErsatzTV/Pages/Libraries.razor
  18. 48
      ErsatzTV/Pages/LocalLibraryEditor.razor
  19. 2
      ErsatzTV/Pages/LocalMediaSourceEditor.razor
  20. 2
      ErsatzTV/Pages/Playouts.razor
  21. 2
      ErsatzTV/Pages/ScheduleEditor.razor
  22. 4
      ErsatzTV/Pages/ScheduleItemsEditor.razor
  23. 8
      ErsatzTV/Pages/Schedules.razor
  24. 1
      ErsatzTV/Shared/LocalMediaSources.razor
  25. 4
      ErsatzTV/Shared/MainLayout.razor

4
ErsatzTV.Application/Libraries/LocalLibraryPathViewModel.cs

@ -0,0 +1,4 @@
namespace ErsatzTV.Application.Libraries
{
public record LocalLibraryPathViewModel(int Id, string Path);
}

4
ErsatzTV.Application/Libraries/LocalLibraryViewModel.cs

@ -0,0 +1,4 @@
namespace ErsatzTV.Application.Libraries
{
public record LocalLibraryViewModel(int Id, string Name, string MediaKind);
}

13
ErsatzTV.Application/Libraries/Mapper.cs

@ -0,0 +1,13 @@
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Libraries
{
internal static class Mapper
{
public static LocalLibraryViewModel ProjectToViewModel(LocalLibrary library) =>
new(library.Id, library.Name, library.MediaKind.ToString());
public static LocalLibraryPathViewModel ProjectToViewModel(LibraryPath libraryPath) =>
new(libraryPath.Id, libraryPath.Path);
}
}

7
ErsatzTV.Application/Libraries/Queries/GetLocalLibraries.cs

@ -0,0 +1,7 @@
using System.Collections.Generic;
using MediatR;
namespace ErsatzTV.Application.Libraries.Queries
{
public record GetLocalLibraries : IRequest<List<LocalLibraryViewModel>>;
}

22
ErsatzTV.Application/Libraries/Queries/GetLocalLibrariesHandler.cs

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
using static ErsatzTV.Application.Libraries.Mapper;
namespace ErsatzTV.Application.Libraries.Queries
{
public class GetLocalLibrariesHandler : IRequestHandler<GetLocalLibraries, List<LocalLibraryViewModel>>
{
private readonly ILibraryRepository _libraryRepository;
public GetLocalLibrariesHandler(ILibraryRepository libraryRepository) => _libraryRepository = libraryRepository;
public Task<List<LocalLibraryViewModel>>
Handle(GetLocalLibraries request, CancellationToken cancellationToken) =>
_libraryRepository.GetAllLocal().Map(list => list.Map(ProjectToViewModel).ToList());
}
}

7
ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPaths.cs

@ -0,0 +1,7 @@
using System.Collections.Generic;
using MediatR;
namespace ErsatzTV.Application.Libraries.Queries
{
public record GetLocalLibraryPaths(int LocalLibraryId) : IRequest<List<LocalLibraryPathViewModel>>;
}

25
ErsatzTV.Application/Libraries/Queries/GetLocalLibraryPathsHandler.cs

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
using static ErsatzTV.Application.Libraries.Mapper;
namespace ErsatzTV.Application.Libraries.Queries
{
public class GetLocalLibraryPathsHandler : IRequestHandler<GetLocalLibraryPaths, List<LocalLibraryPathViewModel>>
{
private readonly ILibraryRepository _libraryRepository;
public GetLocalLibraryPathsHandler(ILibraryRepository libraryRepository) =>
_libraryRepository = libraryRepository;
public Task<List<LocalLibraryPathViewModel>> Handle(
GetLocalLibraryPaths request,
CancellationToken cancellationToken) =>
_libraryRepository.GetLocalPaths(request.LocalLibraryId)
.Map(list => list.Map(ProjectToViewModel).ToList());
}
}

2
ErsatzTV.Application/MediaCollections/Commands/CreateCollectionHandler.cs

@ -46,7 +46,7 @@ namespace ErsatzTV.Application.MediaCollections.Commands
var result2 = Optional(createCollection.Name) var result2 = Optional(createCollection.Name)
.Filter(name => !allNames.Contains(name)) .Filter(name => !allNames.Contains(name))
.ToValidation<BaseError>("Media collection name must be unique"); .ToValidation<BaseError>("Collection name must be unique");
return (result1, result2).Apply((_, _) => createCollection.Name); return (result1, result2).Apply((_, _) => createCollection.Name);
} }

2
ErsatzTV.CommandLine/Commands/Schedules/ScheduleAddItemCommand.cs

@ -97,7 +97,7 @@ namespace ErsatzTV.CommandLine.Commands.Schedules
() => () =>
{ {
_logger.LogError( _logger.LogError(
"Unable to locate media collection {MediaCollection}", "Unable to locate collection {Collection}",
CollectionName); CollectionName);
return Task.CompletedTask; return Task.CompletedTask;
}); });

5
ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks; using System.Collections.Generic;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using LanguageExt; using LanguageExt;
@ -7,6 +8,8 @@ namespace ErsatzTV.Core.Interfaces.Repositories
public interface ILibraryRepository public interface ILibraryRepository
{ {
Task<Option<Library>> Get(int libraryId); Task<Option<Library>> Get(int libraryId);
Task<List<LocalLibrary>> GetAllLocal();
Task<Unit> UpdateLastScan(Library library); Task<Unit> UpdateLastScan(Library library);
Task<List<LibraryPath>> GetLocalPaths(int libraryId);
} }
} }

20
ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs

@ -1,4 +1,5 @@
using System.Data; using System.Collections.Generic;
using System.Data;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapper; using Dapper;
@ -31,8 +32,25 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.Map(Optional); .Map(Optional);
} }
public Task<List<LocalLibrary>> GetAllLocal()
{
using TvContext context = _dbContextFactory.CreateDbContext();
return context.LocalLibraries.ToListAsync();
}
public Task<Unit> UpdateLastScan(Library library) => _dbConnection.ExecuteAsync( public Task<Unit> UpdateLastScan(Library library) => _dbConnection.ExecuteAsync(
"UPDATE Library SET LastScan = @LastScan WHERE Id = @Id", "UPDATE Library SET LastScan = @LastScan WHERE Id = @Id",
new { library.LastScan, library.Id }).ToUnit(); new { library.LastScan, library.Id }).ToUnit();
public Task<List<LibraryPath>> GetLocalPaths(int libraryId)
{
using TvContext context = _dbContextFactory.CreateDbContext();
return context.LocalLibraries
.Include(l => l.Paths)
.OrderBy(l => l.Id)
.SingleOrDefaultAsync(l => l.Id == libraryId)
.Map(Optional)
.Match(l => l.Paths, () => new List<LibraryPath>());
}
} }
} }

1
ErsatzTV.Infrastructure/Data/TvContext.cs

@ -18,6 +18,7 @@ namespace ErsatzTV.Infrastructure.Data
public DbSet<LocalMediaSource> LocalMediaSources { get; set; } public DbSet<LocalMediaSource> LocalMediaSources { get; set; }
public DbSet<PlexMediaSource> PlexMediaSources { get; set; } public DbSet<PlexMediaSource> PlexMediaSources { get; set; }
public DbSet<Library> Libraries { get; set; } public DbSet<Library> Libraries { get; set; }
public DbSet<LocalLibrary> LocalLibraries { get; set; }
public DbSet<PlexLibrary> PlexLibraries { get; set; } public DbSet<PlexLibrary> PlexLibraries { get; set; }
public DbSet<MediaItem> MediaItems { get; set; } public DbSet<MediaItem> MediaItems { get; set; }
public DbSet<Movie> Movies { get; set; } public DbSet<Movie> Movies { get; set; }

8
ErsatzTV/Pages/MediaCollectionEditor.razor → ErsatzTV/Pages/CollectionEditor.razor

@ -4,12 +4,12 @@
@using ErsatzTV.Application.MediaCollections.Commands @using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Application.MediaCollections.Queries @using ErsatzTV.Application.MediaCollections.Queries
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject ILogger<MediaCollectionEditor> Logger @inject ILogger<CollectionEditor> Logger
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@inject IMediator Mediator @inject IMediator Mediator
<div style="max-width: 400px;"> <div style="max-width: 400px;">
<MudText Typo="Typo.h4" Class="mb-4">@(IsEdit ? "Edit Media Collection" : "Add Media Collection")</MudText> <MudText Typo="Typo.h4" Class="mb-4">@(IsEdit ? "Edit Collection" : "Add Collection")</MudText>
<EditForm EditContext="_editContext" OnSubmit="@HandleSubmitAsync"> <EditForm EditContext="_editContext" OnSubmit="@HandleSubmitAsync">
<FluentValidator/> <FluentValidator/>
@ -19,7 +19,7 @@
</MudCardContent> </MudCardContent>
<MudCardActions> <MudCardActions>
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary" Class="ml-auto"> <MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary" Class="ml-auto">
@(IsEdit ? "Save Changes" : "Add Media Collection") @(IsEdit ? "Save Changes" : "Add Collection")
</MudButton> </MudButton>
</MudCardActions> </MudCardActions>
</MudCard> </MudCard>
@ -73,7 +73,7 @@
error => error =>
{ {
Snackbar.Add(error.Value, Severity.Error); Snackbar.Add(error.Value, Severity.Error);
Logger.LogError("Error saving simple media collection: {Error}", error.Value); Logger.LogError("Error saving collection: {Error}", error.Value);
}, },
() => NavigationManager.NavigateTo(_model.Id > 0 ? $"/media/collections/{_model.Id}" : "/media/collections")); () => NavigationManager.NavigateTo(_model.Id > 0 ? $"/media/collections/{_model.Id}" : "/media/collections"));
} }

2
ErsatzTV/Pages/MediaCollectionItems.razor → ErsatzTV/Pages/CollectionItems.razor

@ -4,7 +4,7 @@
@using ErsatzTV.Application.MediaCollections.Commands @using ErsatzTV.Application.MediaCollections.Commands
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IMediator Mediator @inject IMediator Mediator
@inject ILogger<MediaCollectionItems> Logger @inject ILogger<CollectionItems> Logger
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@inject IDialogService Dialog @inject IDialogService Dialog

6
ErsatzTV/Pages/MediaCollections.razor → ErsatzTV/Pages/Collections.razor

@ -19,7 +19,7 @@
<MudContainer MaxWidth="MaxWidth.False"> <MudContainer MaxWidth="MaxWidth.False">
<MudButton Variant="Variant.Filled" Color="Color.Primary" Link="/media/collections/add" Class="mt-4"> <MudButton Variant="Variant.Filled" Color="Color.Primary" Link="/media/collections/add" Class="mt-4">
Add Media Collection Add Collection
</MudButton> </MudButton>
</MudContainer> </MudContainer>
@ -35,10 +35,10 @@
{ {
if (vm is MediaCollectionViewModel collection) if (vm is MediaCollectionViewModel collection)
{ {
var parameters = new DialogParameters { { "EntityType", "media collection" }, { "EntityName", collection.Name } }; var parameters = new DialogParameters { { "EntityType", "collection" }, { "EntityName", collection.Name } };
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall };
IDialogReference dialog = Dialog.Show<DeleteDialog>("Delete Media Collection", parameters, options); IDialogReference dialog = Dialog.Show<DeleteDialog>("Delete Collection", parameters, options);
DialogResult result = await dialog.Result; DialogResult result = await dialog.Result;
if (!result.Cancelled) if (!result.Cancelled)
{ {

6
ErsatzTV/Pages/Index.razor

@ -28,9 +28,9 @@
</MudText> </MudText>
</MudElement> </MudElement>
<MudElement HtmlTag="div" Class="mt-6"> <MudElement HtmlTag="div" Class="mt-6">
<MudText Typo="Typo.h4" GutterBottom="true">Media Collections</MudText> <MudText Typo="Typo.h4" GutterBottom="true">Collections</MudText>
<MudText> <MudText>
<MudLink Href="/media/collections">Media collections</MudLink> have a <b>name</b> and contain a logical grouping of media items. <MudLink Href="/media/collections">Collections</MudLink> have a <b>name</b> and contain a logical grouping of media items.
Collections may contain television shows, television seasons, television episodes or movies. Collections may contain television shows, television seasons, television episodes or movies.
Collections containing television shows and television seasons are automatically updated as media is added or removed from the linked shows and seasons. Collections containing television shows and television seasons are automatically updated as media is added or removed from the linked shows and seasons.
</MudText> </MudText>
@ -47,7 +47,7 @@
<li><b>Chronological</b> - to play collection items sorted by air date and then by season and episode number (for when multiple episodes aired on a single day).</li> <li><b>Chronological</b> - to play collection items sorted by air date and then by season and episode number (for when multiple episodes aired on a single day).</li>
</ul> </ul>
<MudText Class="mt-3"> <MudText Class="mt-3">
Schedule items have a <b>start type</b>, a <b>start time</b>, a <b>media collection</b> and a <b>playout mode</b>. Schedule items have a <b>start type</b>, a <b>start time</b>, a <b>collection</b> and a <b>playout mode</b>.
</MudText> </MudText>
<MudText Class="mt-3"> <MudText Class="mt-3">
A <b>fixed</b> start type requires a <b>start time</b>, while a <b>dynamic</b> start type means the schedule item will start immediately after the preceding schedule item. A <b>fixed</b> start type requires a <b>start time</b>, while a <b>dynamic</b> start type means the schedule item will start immediately after the preceding schedule item.

82
ErsatzTV/Pages/Libraries.razor

@ -0,0 +1,82 @@
@page "/media/libraries"
@using ErsatzTV.Application.Libraries
@using ErsatzTV.Application.Libraries.Queries
@implements IDisposable
@inject IMediator Mediator
@inject IEntityLocker Locker
<MudContainer MaxWidth="MaxWidth.ExtraLarge">
<MudTable Hover="true" Items="_libraries" Dense="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Libraries</MudText>
</ToolBarContent>
<ColGroup>
<col/>
<col/>
<col/>
<col style="width: 120px;"/>
</ColGroup>
<HeaderContent>
<MudTh>Library Kind</MudTh>
<MudTh>Name</MudTh>
<MudTh>Media Kind</MudTh>
<MudTh/>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Library Kind">Local</MudTd>
<MudTd DataLabel="Name">@context.Name</MudTd>
<MudTd DataLabel="Media Kind">@context.MediaKind</MudTd>
<MudTd>
<div style="align-items: center; display: flex;">
@if (Locker.IsLibraryLocked(context.Id))
{
<div style="align-items: center; display: flex; height: 48px; justify-content: center; width: 48px;">
<MudProgressCircular Color="Color.Primary" Size="Size.Small" Indeterminate="true"/>
</div>
}
else
{
<MudTooltip Text="Scan Library">
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
Disabled="@Locker.IsLibraryLocked(context.Id)"
OnClick="@(_ => ScanLibrary(context))">
</MudIconButton>
</MudTooltip>
}
@if (context is LocalLibraryViewModel)
{
<MudTooltip Text="Edit Library">
<MudIconButton Icon="@Icons.Material.Filled.Edit"
Disabled="@Locker.IsLibraryLocked(context.Id)"
Link="@($"/media/libraries/local/{context.Id}")">
</MudIconButton>
</MudTooltip>
}
</div>
</MudTd>
</RowTemplate>
</MudTable>
</MudContainer>
@code {
private IList<LocalLibraryViewModel> _libraries;
protected override void OnInitialized() =>
Locker.OnLibraryChanged += LockChanged;
protected override async Task OnParametersSetAsync() => await LoadLibraries();
private async Task LoadLibraries() =>
_libraries = await Mediator.Send(new GetLocalLibraries());
private Task ScanLibrary(LocalLibraryViewModel library)
{
return Task.CompletedTask;
}
private void LockChanged(object sender, EventArgs e) =>
InvokeAsync(StateHasChanged);
void IDisposable.Dispose() => Locker.OnLibraryChanged -= LockChanged;
}

48
ErsatzTV/Pages/LocalLibraryEditor.razor

@ -0,0 +1,48 @@
@page "/media/libraries/local/{Id:int}"
@using ErsatzTV.Application.Libraries
@using ErsatzTV.Application.Libraries.Queries
@inject IMediator Mediator
<MudContainer MaxWidth="MaxWidth.ExtraLarge">
<MudTable Hover="true" Items="_libraryPaths" Dense="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Library Paths</MudText>
</ToolBarContent>
<ColGroup>
<col/>
<col style="width: 60px;"/>
</ColGroup>
<HeaderContent>
<MudTh>Path</MudTh>
<MudTh/>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Path">@context.Path</MudTd>
<MudTd>
<div style="align-items: center; display: flex;">
<MudTooltip Text="Delete Library Path">
<MudIconButton Icon="@Icons.Material.Filled.Delete"
OnClick="@(() => DeleteLibraryPath(context))">
</MudIconButton>
</MudTooltip>
</div>
</MudTd>
</RowTemplate>
</MudTable>
</MudContainer>
@code {
[Parameter]
public int Id { get; set; }
private IList<LocalLibraryPathViewModel> _libraryPaths;
protected override async Task OnParametersSetAsync() => await LoadLibraryPaths();
private async Task LoadLibraryPaths() =>
_libraryPaths = await Mediator.Send(new GetLocalLibraryPaths(Id));
private Task DeleteLibraryPath(LocalLibraryPathViewModel libraryPath) => Task.CompletedTask;
}

2
ErsatzTV/Pages/LocalMediaSourceEditor.razor

@ -80,7 +80,7 @@
@* Left: error => *@ @* Left: error => *@
@* { *@ @* { *@
@* Snackbar.Add(error.Value, Severity.Error); *@ @* Snackbar.Add(error.Value, Severity.Error); *@
@* Logger.LogError("Unexpected error saving simple media collection: {Error}", error.Value); *@ @* Logger.LogError("Unexpected error saving collection: {Error}", error.Value); *@
@* return Task.CompletedTask; *@ @* return Task.CompletedTask; *@
@* }, *@ @* }, *@
@* Right: async vm => *@ @* Right: async vm => *@

2
ErsatzTV/Pages/Playouts.razor

@ -75,7 +75,7 @@
var parameters = new DialogParameters { { "EntityType", "playout" }, { "EntityName", $"{playout.ProgramSchedule.Name} on {playout.Channel.Number} - {playout.Channel.Name}" } }; var parameters = new DialogParameters { { "EntityType", "playout" }, { "EntityName", $"{playout.ProgramSchedule.Name} on {playout.Channel.Number} - {playout.Channel.Name}" } };
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall };
IDialogReference dialog = Dialog.Show<DeleteDialog>("Delete Media Collection", parameters, options); IDialogReference dialog = Dialog.Show<DeleteDialog>("Delete Playout", parameters, options);
DialogResult result = await dialog.Result; DialogResult result = await dialog.Result;
if (!result.Cancelled) if (!result.Cancelled)
{ {

2
ErsatzTV/Pages/ScheduleEditor.razor

@ -15,7 +15,7 @@
<MudCard> <MudCard>
<MudCardContent> <MudCardContent>
<MudTextField Label="Name" @bind-Value="_model.Name" For="@(() => _model.Name)"/> <MudTextField Label="Name" @bind-Value="_model.Name" For="@(() => _model.Name)"/>
<MudSelect Class="mt-3" Label="Media Collection Playback Order" @bind-Value="_model.MediaCollectionPlaybackOrder" For="@(() => _model.MediaCollectionPlaybackOrder)"> <MudSelect Class="mt-3" Label="Collection Playback Order" @bind-Value="_model.MediaCollectionPlaybackOrder" For="@(() => _model.MediaCollectionPlaybackOrder)">
@foreach (PlaybackOrder playbackOrder in Enum.GetValues<PlaybackOrder>()) @foreach (PlaybackOrder playbackOrder in Enum.GetValues<PlaybackOrder>())
{ {
<MudSelectItem Value="@playbackOrder">@playbackOrder</MudSelectItem> <MudSelectItem Value="@playbackOrder">@playbackOrder</MudSelectItem>

4
ErsatzTV/Pages/ScheduleItemsEditor.razor

@ -25,7 +25,7 @@
</ColGroup> </ColGroup>
<HeaderContent> <HeaderContent>
<MudTh>Start Time</MudTh> <MudTh>Start Time</MudTh>
<MudTh>Media Collection</MudTh> <MudTh>Collection</MudTh>
<MudTh>Playout Mode</MudTh> <MudTh>Playout Mode</MudTh>
<MudTh/> <MudTh/>
<MudTh/> <MudTh/>
@ -104,7 +104,7 @@
{ {
<MudAutocomplete Class="mt-3" <MudAutocomplete Class="mt-3"
T="MediaCollectionViewModel" T="MediaCollectionViewModel"
Label="Media Collection" Label="Collection"
@bind-value="_selectedItem.Collection" @bind-value="_selectedItem.Collection"
SearchFunc="@SearchMediaCollections" SearchFunc="@SearchMediaCollections"
ToStringFunc="@(c => c?.Name)"/> ToStringFunc="@(c => c?.Name)"/>

8
ErsatzTV/Pages/Schedules.razor

@ -19,7 +19,7 @@
<HeaderContent> <HeaderContent>
<MudTh>Id</MudTh> <MudTh>Id</MudTh>
<MudTh>Name</MudTh> <MudTh>Name</MudTh>
<MudTh>Media Collection Playback Order</MudTh> <MudTh>Collection Playback Order</MudTh>
<MudTh/> <MudTh/>
</HeaderContent> </HeaderContent>
<RowTemplate> <RowTemplate>
@ -54,15 +54,15 @@
</ToolBarContent> </ToolBarContent>
<HeaderContent> <HeaderContent>
<MudTh>Start Time</MudTh> <MudTh>Start Time</MudTh>
<MudTh>Media Collection</MudTh> <MudTh>Collection</MudTh>
<MudTh>Playout Mode</MudTh> <MudTh>Playout Mode</MudTh>
</HeaderContent> </HeaderContent>
<RowTemplate> <RowTemplate>
<MudTd DataLabel="Start Time"> <MudTd DataLabel="Start Time">
@(context.StartType == StartType.Fixed ? context.StartTime?.ToString(@"hh\:mm") ?? string.Empty : "Dynamic") @(context.StartType == StartType.Fixed ? context.StartTime?.ToString(@"hh\:mm") ?? string.Empty : "Dynamic")
</MudTd> </MudTd>
<MudTd DataLabel="Media Collection">@context.Name</MudTd> <MudTd DataLabel="Collection">@context.Name</MudTd>
<MudTd DataLabel="Media Collection">@context.PlayoutMode</MudTd> <MudTd DataLabel="Playout Mode">@context.PlayoutMode</MudTd>
</RowTemplate> </RowTemplate>
<PagerContent> <PagerContent>
<MudTablePager/> <MudTablePager/>

1
ErsatzTV/Shared/LocalMediaSources.razor

@ -1,6 +1,7 @@
@using ErsatzTV.Application.MediaSources @using ErsatzTV.Application.MediaSources
@using ErsatzTV.Application.MediaSources.Commands @using ErsatzTV.Application.MediaSources.Commands
@using ErsatzTV.Application.MediaSources.Queries @using ErsatzTV.Application.MediaSources.Queries
@using ErsatzTV.Application.Libraries
@implements IDisposable @implements IDisposable
@inject IDialogService Dialog @inject IDialogService Dialog
@inject IMediator Mediator @inject IMediator Mediator

4
ErsatzTV/Shared/MainLayout.razor

@ -27,10 +27,10 @@
<MudNavLink Href="/channels">Channels</MudNavLink> <MudNavLink Href="/channels">Channels</MudNavLink>
<MudNavLink Href="/ffmpeg">FFmpeg</MudNavLink> <MudNavLink Href="/ffmpeg">FFmpeg</MudNavLink>
<MudNavGroup Title="Media" Expanded="true"> <MudNavGroup Title="Media" Expanded="true">
<MudNavLink Href="/media/sources">Media Sources</MudNavLink> <MudNavLink Href="/media/libraries">Libraries</MudNavLink>
<MudNavLink Href="/media/tv/shows">TV Shows</MudNavLink> <MudNavLink Href="/media/tv/shows">TV Shows</MudNavLink>
<MudNavLink Href="/media/movies">Movies</MudNavLink> <MudNavLink Href="/media/movies">Movies</MudNavLink>
<MudNavLink Href="/media/collections">Media Collections</MudNavLink> <MudNavLink Href="/media/collections">Collections</MudNavLink>
</MudNavGroup> </MudNavGroup>
<MudNavLink Href="/schedules">Schedules</MudNavLink> <MudNavLink Href="/schedules">Schedules</MudNavLink>
<MudNavLink Href="/playouts">Playouts</MudNavLink> <MudNavLink Href="/playouts">Playouts</MudNavLink>

Loading…
Cancel
Save