Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
871a031467 | ||
|
|
98cf922b3c | ||
|
|
8fb23f2edb | ||
|
|
1aac2f13c9 | ||
|
|
2c9d4d796a | ||
|
|
9d40caebd6 | ||
|
|
0b5a6f9dcd | ||
|
|
76495c1f7b | ||
|
|
d0d1186b92 | ||
|
|
04ab4ee60f | ||
|
|
e62074cc26 | ||
|
|
db054ece24 | ||
|
|
c2d8a54a47 | ||
|
|
88b645af2d | ||
|
|
941f1a59ee | ||
|
|
a3e20826a5 | ||
|
|
ebff29d6cd | ||
|
|
5a29fc1cbb |
@@ -39,28 +39,29 @@ jobs:
|
||||
# Define some variables for things we need
|
||||
tag=$(git describe --tags --abbrev=0)
|
||||
release_name="ErsatzTV-$tag-${{ matrix.target }}"
|
||||
release_name_cli="ErsatzTV.CommandLine-$tag-${{ matrix.target }}"
|
||||
#release_name_cli="ErsatzTV.CommandLine-$tag-${{ matrix.target }}"
|
||||
|
||||
# Build everything
|
||||
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name"
|
||||
dotnet publish ErsatzTV.CommandLine/ErsatzTV.CommandLine.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name_cli"
|
||||
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name" /property:InformationalVersion="${tag:1}-${{ matrix.target }}"
|
||||
#dotnet publish ErsatzTV.CommandLine/ErsatzTV.CommandLine.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name_cli" /property:InformationalVersion="${tag:1}-${{ matrix.target }}"
|
||||
|
||||
# Pack files
|
||||
if [ "${{ matrix.target }}" == "win-x64" ]; then
|
||||
7z a -tzip "${release_name}.zip" "./${release_name}/*"
|
||||
7z a -tzip "${release_name_cli}.zip" "./${release_name_cli}/*"
|
||||
#7z a -tzip "${release_name_cli}.zip" "./${release_name_cli}/*"
|
||||
else
|
||||
tar czvf "${release_name}.tar.gz" "$release_name"
|
||||
tar czvf "${release_name_cli}.tar.gz" "$release_name_cli"
|
||||
#tar czvf "${release_name_cli}.tar.gz" "$release_name_cli"
|
||||
fi
|
||||
|
||||
# Delete output directory
|
||||
rm -r "$release_name"
|
||||
rm -r "$release_name_cli"
|
||||
#rm -r "$release_name_cli"
|
||||
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
prerelease: true
|
||||
files: |
|
||||
ErsatzTV*.zip
|
||||
ErsatzTV*.tar.gz
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<InformationalVersion>develop</InformationalVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
+2
-1
@@ -26,7 +26,8 @@ COPY ErsatzTV.Core/. ./ErsatzTV.Core/
|
||||
COPY ErsatzTV.Core.Tests/. ./ErsatzTV.Core.Tests/
|
||||
COPY ErsatzTV.Infrastructure/. ./ErsatzTV.Infrastructure/
|
||||
WORKDIR /source/ErsatzTV
|
||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore
|
||||
ARG INFO_VERSION="unknown"
|
||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore /p:InformationalVersion=${INFO_VERSION}
|
||||
|
||||
# final stage/image
|
||||
FROM runtime-base
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Interfaces.Images;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
@@ -11,33 +9,12 @@ namespace ErsatzTV.Application.Images.Commands
|
||||
{
|
||||
public class SaveImageToDiskHandler : IRequestHandler<SaveImageToDisk, Either<BaseError, string>>
|
||||
{
|
||||
private static readonly SHA1CryptoServiceProvider Crypto;
|
||||
private readonly IImageCache _imageCache;
|
||||
|
||||
static SaveImageToDiskHandler() => Crypto = new SHA1CryptoServiceProvider();
|
||||
public SaveImageToDiskHandler(IImageCache imageCache) => _imageCache = imageCache;
|
||||
|
||||
public async Task<Either<BaseError, string>> Handle(
|
||||
public Task<Either<BaseError, string>> Handle(
|
||||
SaveImageToDisk request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] hash = Crypto.ComputeHash(request.Buffer);
|
||||
string hex = BitConverter.ToString(hash).Replace("-", string.Empty);
|
||||
|
||||
string fileName = Path.Combine(FileSystemLayout.ImageCacheFolder, hex);
|
||||
|
||||
if (!Directory.Exists(FileSystemLayout.ImageCacheFolder))
|
||||
{
|
||||
Directory.CreateDirectory(FileSystemLayout.ImageCacheFolder);
|
||||
}
|
||||
|
||||
await File.WriteAllBytesAsync(fileName, request.Buffer, cancellationToken);
|
||||
return hex;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BaseError.New(ex.Message);
|
||||
}
|
||||
}
|
||||
CancellationToken cancellationToken) => _imageCache.SaveImage(request.Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace ErsatzTV.Application.Logs
|
||||
{
|
||||
public record LogEntryViewModel(
|
||||
int Id,
|
||||
DateTime Timestamp,
|
||||
string Level,
|
||||
string Exception,
|
||||
string RenderedMessage,
|
||||
string Properties);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.Logs
|
||||
{
|
||||
internal static class Mapper
|
||||
{
|
||||
internal static LogEntryViewModel ProjectToViewModel(LogEntry logEntry) =>
|
||||
new(
|
||||
logEntry.Id,
|
||||
logEntry.Timestamp,
|
||||
logEntry.Level,
|
||||
logEntry.Exception,
|
||||
logEntry.RenderedMessage,
|
||||
logEntry.Properties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Logs.Queries
|
||||
{
|
||||
public record GetRecentLogEntries : IRequest<List<LogEntryViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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.Logs.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Logs.Queries
|
||||
{
|
||||
public class GetRecentLogEntriesHandler : IRequestHandler<GetRecentLogEntries, List<LogEntryViewModel>>
|
||||
{
|
||||
private readonly ILogRepository _logRepository;
|
||||
|
||||
public GetRecentLogEntriesHandler(ILogRepository logRepository) => _logRepository = logRepository;
|
||||
|
||||
public Task<List<LogEntryViewModel>> Handle(GetRecentLogEntries request, CancellationToken cancellationToken) =>
|
||||
_logRepository.GetRecentLogEntries().Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
internal static class Mapper
|
||||
{
|
||||
internal static TelevisionShowCardViewModel ProjectToViewModel(TelevisionShow televisionShow) =>
|
||||
new(
|
||||
televisionShow.Id,
|
||||
televisionShow.Metadata?.Title,
|
||||
televisionShow.Metadata?.Year.ToString(),
|
||||
televisionShow.Metadata?.SortTitle,
|
||||
televisionShow.Poster);
|
||||
|
||||
internal static TelevisionSeasonCardViewModel ProjectToViewModel(TelevisionSeason televisionSeason) =>
|
||||
new(
|
||||
televisionSeason.TelevisionShow.Metadata?.Title,
|
||||
televisionSeason.Id,
|
||||
televisionSeason.Number,
|
||||
GetSeasonName(televisionSeason.Number),
|
||||
string.Empty,
|
||||
GetSeasonName(televisionSeason.Number),
|
||||
televisionSeason.Poster,
|
||||
televisionSeason.Number == 0 ? "S" : televisionSeason.Number.ToString());
|
||||
|
||||
internal static TelevisionEpisodeCardViewModel ProjectToViewModel(
|
||||
TelevisionEpisodeMediaItem televisionEpisode) =>
|
||||
new(
|
||||
televisionEpisode.Id,
|
||||
televisionEpisode.Metadata?.Aired ?? DateTime.MinValue,
|
||||
televisionEpisode.Season.TelevisionShow.Metadata.Title,
|
||||
televisionEpisode.Metadata?.Title,
|
||||
$"Episode {televisionEpisode.Metadata?.Episode}",
|
||||
televisionEpisode.Metadata?.Episode.ToString(),
|
||||
televisionEpisode.Poster,
|
||||
televisionEpisode.Metadata?.Episode.ToString());
|
||||
|
||||
internal static MovieCardViewModel ProjectToViewModel(MovieMediaItem movie) =>
|
||||
new(
|
||||
movie.Id,
|
||||
movie.Metadata?.Title,
|
||||
movie.Metadata?.Year?.ToString(),
|
||||
movie.Metadata?.SortTitle,
|
||||
movie.Poster);
|
||||
|
||||
internal static SimpleMediaCollectionCardResultsViewModel
|
||||
ProjectToViewModel(SimpleMediaCollection collection) =>
|
||||
new(
|
||||
collection.Name,
|
||||
collection.Movies.Map(ProjectToViewModel).ToList(),
|
||||
collection.TelevisionShows.Map(ProjectToViewModel).ToList(),
|
||||
collection.TelevisionSeasons.Map(ProjectToViewModel).ToList(),
|
||||
collection.TelevisionEpisodes.Map(ProjectToViewModel).ToList());
|
||||
|
||||
private static string GetSeasonName(int number) =>
|
||||
number == 0 ? "Specials" : $"Season {number}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record MediaCardViewModel(string Title, string Subtitle, string SortTitle, string Poster);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record MovieCardResultsViewModel(int Count, List<MovieCardViewModel> Cards);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record MovieCardViewModel
|
||||
(int MovieId, string Title, string Subtitle, string SortTitle, string Poster) : MediaCardViewModel(
|
||||
Title,
|
||||
Subtitle,
|
||||
SortTitle,
|
||||
Poster)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public record GetMovieCards(int PageNumber, int PageSize) : IRequest<MovieCardResultsViewModel>;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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.MediaCards.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public class
|
||||
GetMovieCardsHandler : IRequestHandler<GetMovieCards, MovieCardResultsViewModel>
|
||||
{
|
||||
private readonly IMovieRepository _movieRepository;
|
||||
|
||||
public GetMovieCardsHandler(IMovieRepository movieRepository) => _movieRepository = movieRepository;
|
||||
|
||||
public async Task<MovieCardResultsViewModel> Handle(
|
||||
GetMovieCards request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
int count = await _movieRepository.GetMovieCount();
|
||||
|
||||
List<MovieCardViewModel> results = await _movieRepository
|
||||
.GetPagedMovies(request.PageNumber, request.PageSize)
|
||||
.Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
|
||||
return new MovieCardResultsViewModel(count, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public record GetSimpleMediaCollectionCards
|
||||
(int Id) : IRequest<Either<BaseError, SimpleMediaCollectionCardResultsViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.MediaCards.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public class GetSimpleMediaCollectionCardsHandler : IRequestHandler<GetSimpleMediaCollectionCards,
|
||||
Either<BaseError, SimpleMediaCollectionCardResultsViewModel>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
|
||||
public GetSimpleMediaCollectionCardsHandler(IMediaCollectionRepository mediaCollectionRepository) =>
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
|
||||
public async Task<Either<BaseError, SimpleMediaCollectionCardResultsViewModel>> Handle(
|
||||
GetSimpleMediaCollectionCards request,
|
||||
CancellationToken cancellationToken) =>
|
||||
(await _mediaCollectionRepository.GetSimpleMediaCollectionWithItemsUntracked(request.Id))
|
||||
.ToEither(BaseError.New("Unable to load collection"))
|
||||
.Map(ProjectToViewModel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public record GetTelevisionEpisodeCards
|
||||
(int TelevisionSeasonId, int PageNumber, int PageSize) : IRequest<TelevisionEpisodeCardResultsViewModel>;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
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.MediaCards.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public class
|
||||
GetTelevisionEpisodeCardsHandler : IRequestHandler<GetTelevisionEpisodeCards,
|
||||
TelevisionEpisodeCardResultsViewModel>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetTelevisionEpisodeCardsHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public async Task<TelevisionEpisodeCardResultsViewModel> Handle(
|
||||
GetTelevisionEpisodeCards request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
int count = await _televisionRepository.GetEpisodeCount(request.TelevisionSeasonId);
|
||||
|
||||
List<TelevisionEpisodeCardViewModel> results = await _televisionRepository
|
||||
.GetPagedEpisodes(request.TelevisionSeasonId, request.PageNumber, request.PageSize)
|
||||
.Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
|
||||
return new TelevisionEpisodeCardResultsViewModel(count, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public record GetTelevisionSeasonCards
|
||||
(int TelevisionShowId, int PageNumber, int PageSize) : IRequest<TelevisionSeasonCardResultsViewModel>;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
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.MediaCards.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public class
|
||||
GetTelevisionSeasonCardsHandler : IRequestHandler<GetTelevisionSeasonCards, TelevisionSeasonCardResultsViewModel
|
||||
>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetTelevisionSeasonCardsHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public async Task<TelevisionSeasonCardResultsViewModel> Handle(
|
||||
GetTelevisionSeasonCards request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
int count = await _televisionRepository.GetSeasonCount(request.TelevisionShowId);
|
||||
|
||||
List<TelevisionSeasonCardViewModel> results = await _televisionRepository
|
||||
.GetPagedSeasons(request.TelevisionShowId, request.PageNumber, request.PageSize)
|
||||
.Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
|
||||
return new TelevisionSeasonCardResultsViewModel(count, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public record GetTelevisionShowCards(int PageNumber, int PageSize) : IRequest<TelevisionShowCardResultsViewModel>;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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.MediaCards.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards.Queries
|
||||
{
|
||||
public class
|
||||
GetTelevisionShowCardsHandler : IRequestHandler<GetTelevisionShowCards, TelevisionShowCardResultsViewModel>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetTelevisionShowCardsHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public async Task<TelevisionShowCardResultsViewModel> Handle(
|
||||
GetTelevisionShowCards request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
int count = await _televisionRepository.GetShowCount();
|
||||
|
||||
List<TelevisionShowCardViewModel> results = await _televisionRepository
|
||||
.GetPagedShows(request.PageNumber, request.PageSize)
|
||||
.Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
|
||||
return new TelevisionShowCardResultsViewModel(count, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record SimpleMediaCollectionCardResultsViewModel(
|
||||
string Name,
|
||||
List<MovieCardViewModel> MovieCards,
|
||||
List<TelevisionShowCardViewModel> ShowCards,
|
||||
List<TelevisionSeasonCardViewModel> SeasonCards,
|
||||
List<TelevisionEpisodeCardViewModel> EpisodeCards);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record TelevisionEpisodeCardResultsViewModel(int Count, List<TelevisionEpisodeCardViewModel> Cards);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record TelevisionEpisodeCardViewModel
|
||||
(
|
||||
int EpisodeId,
|
||||
DateTime Aired,
|
||||
string ShowTitle,
|
||||
string Title,
|
||||
string Subtitle,
|
||||
string SortTitle,
|
||||
string Poster,
|
||||
string Placeholder) : MediaCardViewModel(
|
||||
Title,
|
||||
Subtitle,
|
||||
SortTitle,
|
||||
Poster)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record TelevisionSeasonCardResultsViewModel(int Count, List<TelevisionSeasonCardViewModel> Cards);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record TelevisionSeasonCardViewModel
|
||||
(
|
||||
string ShowTitle,
|
||||
int TelevisionSeasonId,
|
||||
int TelevisionSeasonNumber,
|
||||
string Title,
|
||||
string Subtitle,
|
||||
string SortTitle,
|
||||
string Poster,
|
||||
string Placeholder) : MediaCardViewModel(
|
||||
Title,
|
||||
Subtitle,
|
||||
SortTitle,
|
||||
Poster)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record TelevisionShowCardResultsViewModel(int Count, List<TelevisionShowCardViewModel> Cards);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace ErsatzTV.Application.MediaCards
|
||||
{
|
||||
public record TelevisionShowCardViewModel
|
||||
(int TelevisionShowId, string Title, string Subtitle, string SortTitle, string Poster) : MediaCardViewModel(
|
||||
Title,
|
||||
Subtitle,
|
||||
SortTitle,
|
||||
Poster)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public record AddMovieToSimpleMediaCollection
|
||||
(int MediaCollectionId, int MovieId) : MediatR.IRequest<Either<BaseError, Unit>>;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public class
|
||||
AddMovieToSimpleMediaCollectionHandler : MediatR.IRequestHandler<AddMovieToSimpleMediaCollection,
|
||||
Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly IMovieRepository _movieRepository;
|
||||
|
||||
public AddMovieToSimpleMediaCollectionHandler(
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
IMovieRepository movieRepository)
|
||||
{
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
_movieRepository = movieRepository;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
AddMovieToSimpleMediaCollection request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(ApplyAddMoviesRequest)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<Unit> ApplyAddMoviesRequest(RequestParameters parameters)
|
||||
{
|
||||
parameters.Collection.Movies.Add(parameters.MovieToAdd);
|
||||
await _mediaCollectionRepository.Update(parameters.Collection);
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, RequestParameters>>
|
||||
Validate(AddMovieToSimpleMediaCollection request) =>
|
||||
(await SimpleMediaCollectionMustExist(request), await ValidateMovies(request))
|
||||
.Apply(
|
||||
(simpleMediaCollectionToUpdate, movieToAdd) =>
|
||||
new RequestParameters(simpleMediaCollectionToUpdate, movieToAdd));
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist(
|
||||
AddMovieToSimpleMediaCollection updateSimpleMediaCollection) =>
|
||||
_mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId)
|
||||
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist."));
|
||||
|
||||
private Task<Validation<BaseError, MovieMediaItem>> ValidateMovies(
|
||||
AddMovieToSimpleMediaCollection request) =>
|
||||
LoadMovie(request)
|
||||
.Map(v => v.ToValidation<BaseError>("MovieMediaItem does not exist"));
|
||||
|
||||
private Task<Option<MovieMediaItem>> LoadMovie(AddMovieToSimpleMediaCollection request) =>
|
||||
_movieRepository.GetMovie(request.MovieId);
|
||||
|
||||
private record RequestParameters(SimpleMediaCollection Collection, MovieMediaItem MovieToAdd);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public record AddTelevisionEpisodeToSimpleMediaCollection
|
||||
(int MediaCollectionId, int TelevisionEpisodeId) : MediatR.IRequest<Either<BaseError, Unit>>;
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public class
|
||||
AddTelevisionEpisodeToSimpleMediaCollectionHandler : MediatR.IRequestHandler<
|
||||
AddTelevisionEpisodeToSimpleMediaCollection,
|
||||
Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public AddTelevisionEpisodeToSimpleMediaCollectionHandler(
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
ITelevisionRepository televisionRepository)
|
||||
{
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
_televisionRepository = televisionRepository;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
AddTelevisionEpisodeToSimpleMediaCollection request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(ApplyAddTelevisionEpisodeRequest)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<Unit> ApplyAddTelevisionEpisodeRequest(RequestParameters parameters)
|
||||
{
|
||||
if (parameters.Collection.TelevisionEpisodes.All(s => s.Id != parameters.EpisodeToAdd.Id))
|
||||
{
|
||||
parameters.Collection.TelevisionEpisodes.Add(parameters.EpisodeToAdd);
|
||||
await _mediaCollectionRepository.Update(parameters.Collection);
|
||||
}
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, RequestParameters>>
|
||||
Validate(AddTelevisionEpisodeToSimpleMediaCollection request) =>
|
||||
(await SimpleMediaCollectionMustExist(request), await ValidateEpisode(request))
|
||||
.Apply(
|
||||
(simpleMediaCollectionToUpdate, episode) =>
|
||||
new RequestParameters(simpleMediaCollectionToUpdate, episode));
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist(
|
||||
AddTelevisionEpisodeToSimpleMediaCollection updateSimpleMediaCollection) =>
|
||||
_mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId)
|
||||
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist."));
|
||||
|
||||
private Task<Validation<BaseError, TelevisionEpisodeMediaItem>> ValidateEpisode(
|
||||
AddTelevisionEpisodeToSimpleMediaCollection request) =>
|
||||
LoadTelevisionEpisode(request)
|
||||
.Map(v => v.ToValidation<BaseError>("TelevisionEpisode does not exist"));
|
||||
|
||||
private Task<Option<TelevisionEpisodeMediaItem>> LoadTelevisionEpisode(
|
||||
AddTelevisionEpisodeToSimpleMediaCollection request) =>
|
||||
_televisionRepository.GetEpisode(request.TelevisionEpisodeId);
|
||||
|
||||
private record RequestParameters(SimpleMediaCollection Collection, TelevisionEpisodeMediaItem EpisodeToAdd);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public record AddTelevisionSeasonToSimpleMediaCollection
|
||||
(int MediaCollectionId, int TelevisionSeasonId) : MediatR.IRequest<Either<BaseError, Unit>>;
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public class
|
||||
AddTelevisionSeasonToSimpleMediaCollectionHandler : MediatR.IRequestHandler<
|
||||
AddTelevisionSeasonToSimpleMediaCollection,
|
||||
Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public AddTelevisionSeasonToSimpleMediaCollectionHandler(
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
ITelevisionRepository televisionRepository)
|
||||
{
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
_televisionRepository = televisionRepository;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
AddTelevisionSeasonToSimpleMediaCollection request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(ApplyAddTelevisionSeasonRequest)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<Unit> ApplyAddTelevisionSeasonRequest(RequestParameters parameters)
|
||||
{
|
||||
if (parameters.Collection.TelevisionSeasons.All(s => s.Id != parameters.SeasonToAdd.Id))
|
||||
{
|
||||
parameters.Collection.TelevisionSeasons.Add(parameters.SeasonToAdd);
|
||||
await _mediaCollectionRepository.Update(parameters.Collection);
|
||||
}
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, RequestParameters>>
|
||||
Validate(AddTelevisionSeasonToSimpleMediaCollection request) =>
|
||||
(await SimpleMediaCollectionMustExist(request), await ValidateSeason(request))
|
||||
.Apply(
|
||||
(simpleMediaCollectionToUpdate, season) =>
|
||||
new RequestParameters(simpleMediaCollectionToUpdate, season));
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist(
|
||||
AddTelevisionSeasonToSimpleMediaCollection updateSimpleMediaCollection) =>
|
||||
_mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId)
|
||||
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist."));
|
||||
|
||||
private Task<Validation<BaseError, TelevisionSeason>> ValidateSeason(
|
||||
AddTelevisionSeasonToSimpleMediaCollection request) =>
|
||||
LoadTelevisionSeason(request)
|
||||
.Map(v => v.ToValidation<BaseError>("TelevisionSeason does not exist"));
|
||||
|
||||
private Task<Option<TelevisionSeason>> LoadTelevisionSeason(
|
||||
AddTelevisionSeasonToSimpleMediaCollection request) =>
|
||||
_televisionRepository.GetSeason(request.TelevisionSeasonId);
|
||||
|
||||
private record RequestParameters(SimpleMediaCollection Collection, TelevisionSeason SeasonToAdd);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public record AddTelevisionShowToSimpleMediaCollection
|
||||
(int MediaCollectionId, int TelevisionShowId) : MediatR.IRequest<Either<BaseError, Unit>>;
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public class
|
||||
AddTelevisionShowToSimpleMediaCollectionHandler : MediatR.IRequestHandler<
|
||||
AddTelevisionShowToSimpleMediaCollection,
|
||||
Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public AddTelevisionShowToSimpleMediaCollectionHandler(
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
ITelevisionRepository televisionRepository)
|
||||
{
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
_televisionRepository = televisionRepository;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
AddTelevisionShowToSimpleMediaCollection request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(ApplyAddTelevisionShowRequest)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<Unit> ApplyAddTelevisionShowRequest(RequestParameters parameters)
|
||||
{
|
||||
if (parameters.Collection.TelevisionShows.All(s => s.Id != parameters.ShowToAdd.Id))
|
||||
{
|
||||
parameters.Collection.TelevisionShows.Add(parameters.ShowToAdd);
|
||||
await _mediaCollectionRepository.Update(parameters.Collection);
|
||||
}
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, RequestParameters>>
|
||||
Validate(AddTelevisionShowToSimpleMediaCollection request) =>
|
||||
(await SimpleMediaCollectionMustExist(request), await ValidateShow(request))
|
||||
.Apply(
|
||||
(simpleMediaCollectionToUpdate, show) =>
|
||||
new RequestParameters(simpleMediaCollectionToUpdate, show));
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist(
|
||||
AddTelevisionShowToSimpleMediaCollection updateSimpleMediaCollection) =>
|
||||
_mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId)
|
||||
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist."));
|
||||
|
||||
private Task<Validation<BaseError, TelevisionShow>> ValidateShow(
|
||||
AddTelevisionShowToSimpleMediaCollection request) =>
|
||||
LoadTelevisionShow(request)
|
||||
.Map(v => v.ToValidation<BaseError>("TelevisionShow does not exist"));
|
||||
|
||||
private Task<Option<TelevisionShow>> LoadTelevisionShow(AddTelevisionShowToSimpleMediaCollection request) =>
|
||||
_televisionRepository.GetShow(request.TelevisionShowId);
|
||||
|
||||
private record RequestParameters(SimpleMediaCollection Collection, TelevisionShow ShowToAdd);
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -29,7 +29,15 @@ namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
_mediaCollectionRepository.Add(c).Map(ProjectToViewModel);
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> Validate(CreateSimpleMediaCollection request) =>
|
||||
ValidateName(request).MapT(name => new SimpleMediaCollection { Name = name });
|
||||
ValidateName(request).MapT(
|
||||
name => new SimpleMediaCollection
|
||||
{
|
||||
Name = name,
|
||||
Movies = new List<MovieMediaItem>(),
|
||||
TelevisionShows = new List<TelevisionShow>(),
|
||||
TelevisionEpisodes = new List<TelevisionEpisodeMediaItem>(),
|
||||
TelevisionSeasons = new List<TelevisionSeason>()
|
||||
});
|
||||
|
||||
private async Task<Validation<BaseError, string>> ValidateName(CreateSimpleMediaCollection createCollection)
|
||||
{
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public record RemoveItemsFromSimpleMediaCollection
|
||||
(int MediaCollectionId) : MediatR.IRequest<Either<BaseError, Unit>>
|
||||
{
|
||||
public List<int> MovieIds { get; set; } = new();
|
||||
public List<int> TelevisionShowIds { get; set; } = new();
|
||||
public List<int> TelevisionSeasonIds { get; set; } = new();
|
||||
public List<int> TelevisionEpisodeIds { get; set; } = new();
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public class
|
||||
RemoveItemsFromSimpleMediaCollectionHandler : MediatR.IRequestHandler<
|
||||
RemoveItemsFromSimpleMediaCollection,
|
||||
Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
|
||||
public RemoveItemsFromSimpleMediaCollectionHandler(
|
||||
IMediaCollectionRepository mediaCollectionRepository) =>
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
RemoveItemsFromSimpleMediaCollection request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(collection => ApplyAddTelevisionEpisodeRequest(request, collection))
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private Task<Unit> ApplyAddTelevisionEpisodeRequest(
|
||||
RemoveItemsFromSimpleMediaCollection request,
|
||||
SimpleMediaCollection collection)
|
||||
{
|
||||
var moviesToRemove = collection.Movies
|
||||
.Filter(m => request.MovieIds.Contains(m.Id))
|
||||
.ToList();
|
||||
|
||||
moviesToRemove.ForEach(m => collection.Movies.Remove(m));
|
||||
|
||||
var showsToRemove = collection.TelevisionShows
|
||||
.Filter(s => request.TelevisionShowIds.Contains(s.Id))
|
||||
.ToList();
|
||||
|
||||
showsToRemove.ForEach(s => collection.TelevisionShows.Remove(s));
|
||||
|
||||
var seasonsToRemove = collection.TelevisionSeasons
|
||||
.Filter(s => request.TelevisionSeasonIds.Contains(s.Id))
|
||||
.ToList();
|
||||
|
||||
seasonsToRemove.ForEach(s => collection.TelevisionSeasons.Remove(s));
|
||||
|
||||
var episodesToRemove = collection.TelevisionEpisodes
|
||||
.Filter(e => request.TelevisionEpisodeIds.Contains(e.Id))
|
||||
.ToList();
|
||||
|
||||
episodesToRemove.ForEach(e => collection.TelevisionEpisodes.Remove(e));
|
||||
|
||||
if (moviesToRemove.Any() || showsToRemove.Any() || seasonsToRemove.Any() || episodesToRemove.Any())
|
||||
{
|
||||
return _mediaCollectionRepository.Update(collection).ToUnit();
|
||||
}
|
||||
|
||||
return Task.FromResult(Unit.Default);
|
||||
}
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> Validate(
|
||||
RemoveItemsFromSimpleMediaCollection request) =>
|
||||
SimpleMediaCollectionMustExist(request);
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist(
|
||||
RemoveItemsFromSimpleMediaCollection updateSimpleMediaCollection) =>
|
||||
_mediaCollectionRepository.GetSimpleMediaCollectionWithItems(updateSimpleMediaCollection.MediaCollectionId)
|
||||
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist."));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using ErsatzTV.Application.MediaItems;
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public record ReplaceSimpleMediaCollectionItems
|
||||
(int MediaCollectionId, List<int> MediaItemIds) : IRequest<Either<BaseError, List<MediaItemViewModel>>>;
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Application.MediaItems;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using LanguageExt.UnsafeValueAccess;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public class ReplaceSimpleMediaCollectionItemsHandler : IRequestHandler<ReplaceSimpleMediaCollectionItems,
|
||||
Either<BaseError, List<MediaItemViewModel>>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public ReplaceSimpleMediaCollectionItemsHandler(
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
IMediaItemRepository mediaItemRepository)
|
||||
{
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, List<MediaItemViewModel>>> Handle(
|
||||
ReplaceSimpleMediaCollectionItems request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(mediaItems => PersistItems(request, mediaItems))
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<List<MediaItemViewModel>> PersistItems(
|
||||
ReplaceSimpleMediaCollectionItems request,
|
||||
List<MediaItem> mediaItems)
|
||||
{
|
||||
await _mediaCollectionRepository.ReplaceItems(request.MediaCollectionId, mediaItems);
|
||||
return mediaItems.Map(MediaItems.Mapper.ProjectToViewModel).ToList();
|
||||
}
|
||||
|
||||
private Task<Validation<BaseError, List<MediaItem>>> Validate(ReplaceSimpleMediaCollectionItems request) =>
|
||||
MediaCollectionMustExist(request).BindT(_ => MediaItemsMustExist(request));
|
||||
|
||||
private async Task<Validation<BaseError, SimpleMediaCollection>> MediaCollectionMustExist(
|
||||
ReplaceSimpleMediaCollectionItems request) =>
|
||||
(await _mediaCollectionRepository.GetSimpleMediaCollection(request.MediaCollectionId))
|
||||
.ToValidation<BaseError>("[MediaCollectionId] does not exist.");
|
||||
|
||||
private async Task<Validation<BaseError, List<MediaItem>>> MediaItemsMustExist(
|
||||
ReplaceSimpleMediaCollectionItems replaceItems)
|
||||
{
|
||||
var allMediaItems = (await replaceItems.MediaItemIds.Map(i => _mediaItemRepository.Get(i)).Sequence())
|
||||
.ToList();
|
||||
if (allMediaItems.Any(x => x.IsNone))
|
||||
{
|
||||
return BaseError.New("[MediaItemId] does not exist");
|
||||
}
|
||||
|
||||
return allMediaItems.Sequence().ValueUnsafe().ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using ErsatzTV.Core.AggregateModels;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections
|
||||
{
|
||||
@@ -7,13 +6,5 @@ namespace ErsatzTV.Application.MediaCollections
|
||||
{
|
||||
internal static MediaCollectionViewModel ProjectToViewModel(MediaCollection mediaCollection) =>
|
||||
new(mediaCollection.Id, mediaCollection.Name);
|
||||
|
||||
internal static MediaCollectionSummaryViewModel ProjectToViewModel(
|
||||
MediaCollectionSummary mediaCollectionSummary) =>
|
||||
new(
|
||||
mediaCollectionSummary.Id,
|
||||
mediaCollectionSummary.Name,
|
||||
mediaCollectionSummary.ItemCount,
|
||||
mediaCollectionSummary.IsSimple);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
namespace ErsatzTV.Application.MediaCollections
|
||||
using ErsatzTV.Application.MediaCards;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections
|
||||
{
|
||||
public record MediaCollectionViewModel(int Id, string Name);
|
||||
public record MediaCollectionViewModel(int Id, string Name) : MediaCardViewModel(
|
||||
Name,
|
||||
string.Empty,
|
||||
Name,
|
||||
string.Empty);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Queries
|
||||
{
|
||||
public record GetMediaCollectionSummaries(string SearchString) : IRequest<List<MediaCollectionSummaryViewModel>>;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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.MediaCollections.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Queries
|
||||
{
|
||||
public class
|
||||
GetMediaCollectionSummariesHandler : IRequestHandler<GetMediaCollectionSummaries,
|
||||
List<MediaCollectionSummaryViewModel>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
|
||||
public GetMediaCollectionSummariesHandler(IMediaCollectionRepository mediaCollectionRepository) =>
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
|
||||
public Task<List<MediaCollectionSummaryViewModel>> Handle(
|
||||
GetMediaCollectionSummaries request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_mediaCollectionRepository.GetSummaries(request.SearchString)
|
||||
.Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
namespace ErsatzTV.Application.MediaItems
|
||||
{
|
||||
public record AggregateMediaItemViewModel(string Source, string Title, int Count, string Duration);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public record CreateMediaItem(int MediaSourceId, string Path) : IRequest<Either<BaseError, MediaItemViewModel>>;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static LanguageExt.Prelude;
|
||||
using static ErsatzTV.Application.MediaItems.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public class CreateMediaItemHandler : IRequestHandler<CreateMediaItem, Either<BaseError, MediaItemViewModel>>
|
||||
{
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ILocalMetadataProvider _localMetadataProvider;
|
||||
private readonly ILocalStatisticsProvider _localStatisticsProvider;
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly ISmartCollectionBuilder _smartCollectionBuilder;
|
||||
|
||||
public CreateMediaItemHandler(
|
||||
IMediaItemRepository mediaItemRepository,
|
||||
IMediaSourceRepository mediaSourceRepository,
|
||||
IConfigElementRepository configElementRepository,
|
||||
ISmartCollectionBuilder smartCollectionBuilder,
|
||||
ILocalMetadataProvider localMetadataProvider,
|
||||
ILocalStatisticsProvider localStatisticsProvider)
|
||||
{
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
_mediaSourceRepository = mediaSourceRepository;
|
||||
_configElementRepository = configElementRepository;
|
||||
_smartCollectionBuilder = smartCollectionBuilder;
|
||||
_localMetadataProvider = localMetadataProvider;
|
||||
_localStatisticsProvider = localStatisticsProvider;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, MediaItemViewModel>> Handle(
|
||||
CreateMediaItem request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(PersistMediaItem)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<MediaItemViewModel> PersistMediaItem(RequestParameters parameters)
|
||||
{
|
||||
await _mediaItemRepository.Add(parameters.MediaItem);
|
||||
|
||||
await _localStatisticsProvider.RefreshStatistics(parameters.FFprobePath, parameters.MediaItem);
|
||||
await _localMetadataProvider.RefreshMetadata(parameters.MediaItem);
|
||||
await _smartCollectionBuilder.RefreshSmartCollections(parameters.MediaItem);
|
||||
|
||||
return ProjectToViewModel(parameters.MediaItem);
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, RequestParameters>> Validate(CreateMediaItem request) =>
|
||||
(await ValidateMediaSource(request), PathMustExist(request), await ValidateFFprobePath())
|
||||
.Apply(
|
||||
(mediaSourceId, path, ffprobePath) => new RequestParameters(
|
||||
ffprobePath,
|
||||
new MediaItem
|
||||
{
|
||||
MediaSourceId = mediaSourceId,
|
||||
Path = path
|
||||
}));
|
||||
|
||||
private async Task<Validation<BaseError, int>> ValidateMediaSource(CreateMediaItem createMediaItem) =>
|
||||
(await MediaSourceMustExist(createMediaItem)).Bind(MediaSourceMustBeLocal);
|
||||
|
||||
private async Task<Validation<BaseError, MediaSource>> MediaSourceMustExist(CreateMediaItem createMediaItem) =>
|
||||
(await _mediaSourceRepository.Get(createMediaItem.MediaSourceId))
|
||||
.ToValidation<BaseError>($"[MediaSource] {createMediaItem.MediaSourceId} does not exist.");
|
||||
|
||||
private Validation<BaseError, int> MediaSourceMustBeLocal(MediaSource mediaSource) =>
|
||||
Some(mediaSource)
|
||||
.Filter(ms => ms is LocalMediaSource)
|
||||
.ToValidation<BaseError>($"[MediaSource] {mediaSource.Id} must be a local media source")
|
||||
.Map(ms => ms.Id);
|
||||
|
||||
private Validation<BaseError, string> PathMustExist(CreateMediaItem createMediaItem) =>
|
||||
Some(createMediaItem.Path)
|
||||
.Filter(File.Exists)
|
||||
.ToValidation<BaseError>("[Path] does not exist on the file system");
|
||||
|
||||
private Task<Validation<BaseError, string>> ValidateFFprobePath() =>
|
||||
_configElementRepository.GetValue<string>(ConfigElementKey.FFprobePath)
|
||||
.FilterT(File.Exists)
|
||||
.Map(
|
||||
ffprobePath =>
|
||||
ffprobePath.ToValidation<BaseError>("FFprobe path does not exist on the file system"));
|
||||
|
||||
private record RequestParameters(string FFprobePath, MediaItem MediaItem);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public record DeleteMediaItem(int MediaItemId) : IRequest<Either<BaseError, Task>>;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public class DeleteMediaItemHandler : IRequestHandler<DeleteMediaItem, Either<BaseError, Task>>
|
||||
{
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public DeleteMediaItemHandler(IMediaItemRepository mediaItemRepository) =>
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
|
||||
public async Task<Either<BaseError, Task>> Handle(
|
||||
DeleteMediaItem request,
|
||||
CancellationToken cancellationToken) =>
|
||||
(await MediaItemMustExist(request))
|
||||
.Map(DoDeletion)
|
||||
.ToEither<Task>();
|
||||
|
||||
private Task DoDeletion(int mediaItemId) => _mediaItemRepository.Delete(mediaItemId);
|
||||
|
||||
private async Task<Validation<BaseError, int>> MediaItemMustExist(DeleteMediaItem deleteMediaItem) =>
|
||||
(await _mediaItemRepository.Get(deleteMediaItem.MediaItemId))
|
||||
.ToValidation<BaseError>($"MediaItem {deleteMediaItem.MediaItemId} does not exist.")
|
||||
.Map(c => c.Id);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public record RefreshMediaItem(int MediaItemId) : MediatR.IRequest<Either<BaseError, Unit>>,
|
||||
IBackgroundServiceRequest;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public record RefreshMediaItemCollections : RefreshMediaItem
|
||||
{
|
||||
public RefreshMediaItemCollections(int mediaItemId) : base(mediaItemId)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public class
|
||||
RefreshMediaItemCollectionsHandler : MediatR.IRequestHandler<RefreshMediaItemCollections,
|
||||
Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
private readonly ISmartCollectionBuilder _smartCollectionBuilder;
|
||||
|
||||
public RefreshMediaItemCollectionsHandler(
|
||||
IMediaItemRepository mediaItemRepository,
|
||||
ISmartCollectionBuilder smartCollectionBuilder)
|
||||
{
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
_smartCollectionBuilder = smartCollectionBuilder;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
RefreshMediaItemCollections request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(RefreshCollections)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private Task<Validation<BaseError, MediaItem>> Validate(RefreshMediaItemCollections request) =>
|
||||
MediaItemMustExist(request);
|
||||
|
||||
private Task<Validation<BaseError, MediaItem>> MediaItemMustExist(
|
||||
RefreshMediaItemCollections refreshMediaItemCollections) =>
|
||||
_mediaItemRepository.Get(refreshMediaItemCollections.MediaItemId)
|
||||
.Map(
|
||||
maybeItem => maybeItem.ToValidation<BaseError>(
|
||||
$"[MediaItem] {refreshMediaItemCollections.MediaItemId} does not exist."));
|
||||
|
||||
private Task<Unit> RefreshCollections(MediaItem mediaItem) =>
|
||||
_smartCollectionBuilder.RefreshSmartCollections(mediaItem).ToUnit();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public record RefreshMediaItemMetadata : RefreshMediaItem
|
||||
{
|
||||
public RefreshMediaItemMetadata(int mediaItemId) : base(mediaItemId)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public class
|
||||
RefreshMediaItemMetadataHandler : MediatR.IRequestHandler<RefreshMediaItemMetadata, Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly ILocalMetadataProvider _localMetadataProvider;
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public RefreshMediaItemMetadataHandler(
|
||||
IMediaItemRepository mediaItemRepository,
|
||||
ILocalMetadataProvider localMetadataProvider)
|
||||
{
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
_localMetadataProvider = localMetadataProvider;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
RefreshMediaItemMetadata request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(RefreshMetadata)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private Task<Validation<BaseError, MediaItem>> Validate(RefreshMediaItemMetadata request) =>
|
||||
MediaItemMustExist(request).BindT(PathMustExist);
|
||||
|
||||
private Task<Validation<BaseError, MediaItem>> MediaItemMustExist(
|
||||
RefreshMediaItemMetadata refreshMediaItemMetadata) =>
|
||||
_mediaItemRepository.Get(refreshMediaItemMetadata.MediaItemId)
|
||||
.Map(
|
||||
maybeItem => maybeItem.ToValidation<BaseError>(
|
||||
$"[MediaItem] {refreshMediaItemMetadata.MediaItemId} does not exist."));
|
||||
|
||||
private Validation<BaseError, MediaItem> PathMustExist(MediaItem mediaItem) =>
|
||||
Some(mediaItem)
|
||||
.Filter(item => File.Exists(item.Path))
|
||||
.ToValidation<BaseError>($"[Path] '{mediaItem.Path}' does not exist on the file system");
|
||||
|
||||
private Task<Unit> RefreshMetadata(MediaItem mediaItem) =>
|
||||
_localMetadataProvider.RefreshMetadata(mediaItem).ToUnit();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public record RefreshMediaItemStatistics : RefreshMediaItem
|
||||
{
|
||||
public RefreshMediaItemStatistics(int mediaItemId) : base(mediaItemId)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Commands
|
||||
{
|
||||
public class
|
||||
RefreshMediaItemStatisticsHandler : MediatR.IRequestHandler<RefreshMediaItemStatistics, Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ILocalStatisticsProvider _localStatisticsProvider;
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public RefreshMediaItemStatisticsHandler(
|
||||
IMediaItemRepository mediaItemRepository,
|
||||
IConfigElementRepository configElementRepository,
|
||||
ILocalStatisticsProvider localStatisticsProvider)
|
||||
{
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
_configElementRepository = configElementRepository;
|
||||
_localStatisticsProvider = localStatisticsProvider;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
RefreshMediaItemStatistics request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(RefreshStatistics)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<Validation<BaseError, RefreshParameters>> Validate(RefreshMediaItemStatistics request) =>
|
||||
(await MediaItemMustExist(request).BindT(PathMustExist), await ValidateFFprobePath())
|
||||
.Apply((mediaItem, ffprobePath) => new RefreshParameters(mediaItem, ffprobePath));
|
||||
|
||||
private Task<Validation<BaseError, MediaItem>> MediaItemMustExist(
|
||||
RefreshMediaItemStatistics refreshMediaItemStatistics) =>
|
||||
_mediaItemRepository.Get(refreshMediaItemStatistics.MediaItemId)
|
||||
.Map(
|
||||
maybeItem => maybeItem.ToValidation<BaseError>(
|
||||
$"[MediaItem] {refreshMediaItemStatistics.MediaItemId} does not exist."));
|
||||
|
||||
private Validation<BaseError, MediaItem> PathMustExist(MediaItem mediaItem) =>
|
||||
Some(mediaItem)
|
||||
.Filter(item => File.Exists(item.Path))
|
||||
.ToValidation<BaseError>($"[Path] '{mediaItem.Path}' does not exist on the file system");
|
||||
|
||||
private Task<Validation<BaseError, string>> ValidateFFprobePath() =>
|
||||
_configElementRepository.GetValue<string>(ConfigElementKey.FFprobePath)
|
||||
.FilterT(File.Exists)
|
||||
.Map(
|
||||
ffprobePath =>
|
||||
ffprobePath.ToValidation<BaseError>("FFprobe path does not exist on the file system"));
|
||||
|
||||
private Task<Unit> RefreshStatistics(RefreshParameters parameters) =>
|
||||
_localStatisticsProvider.RefreshStatistics(parameters.FFprobePath, parameters.MediaItem).ToUnit();
|
||||
|
||||
private record RefreshParameters(MediaItem MediaItem, string FFprobePath);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using System;
|
||||
using System.IO;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems
|
||||
{
|
||||
@@ -9,5 +11,52 @@ namespace ErsatzTV.Application.MediaItems
|
||||
mediaItem.Id,
|
||||
mediaItem.MediaSourceId,
|
||||
mediaItem.Path);
|
||||
|
||||
internal static MediaItemSearchResultViewModel ProjectToSearchViewModel(MediaItem mediaItem) =>
|
||||
mediaItem switch
|
||||
{
|
||||
TelevisionEpisodeMediaItem e => ProjectToSearchViewModel(e),
|
||||
MovieMediaItem m => ProjectToSearchViewModel(m),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
private static MediaItemSearchResultViewModel ProjectToSearchViewModel(TelevisionEpisodeMediaItem mediaItem) =>
|
||||
new(
|
||||
mediaItem.Id,
|
||||
GetSourceName(mediaItem.Source),
|
||||
"TV Show",
|
||||
GetDisplayTitle(mediaItem),
|
||||
GetDisplayDuration(mediaItem));
|
||||
|
||||
private static MediaItemSearchResultViewModel ProjectToSearchViewModel(MovieMediaItem mediaItem) =>
|
||||
new(
|
||||
mediaItem.Id,
|
||||
GetSourceName(mediaItem.Source),
|
||||
"Movie",
|
||||
GetDisplayTitle(mediaItem),
|
||||
GetDisplayDuration(mediaItem));
|
||||
|
||||
|
||||
private static string GetDisplayTitle(MediaItem mediaItem) =>
|
||||
mediaItem switch
|
||||
{
|
||||
TelevisionEpisodeMediaItem e => e.Metadata != null
|
||||
? $"{e.Metadata.Title} - s{e.Metadata.Season:00}e{e.Metadata.Episode:00}"
|
||||
: Path.GetFileName(e.Path),
|
||||
MovieMediaItem m => m.Metadata?.Title ?? Path.GetFileName(m.Path),
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
private static string GetDisplayDuration(MediaItem mediaItem) =>
|
||||
string.Format(
|
||||
mediaItem.Statistics.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}",
|
||||
mediaItem.Statistics.Duration);
|
||||
|
||||
private static string GetSourceName(MediaSource source) =>
|
||||
source switch
|
||||
{
|
||||
LocalMediaSource lms => lms.Folder,
|
||||
_ => source.Name
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace ErsatzTV.Application.MediaItems
|
||||
{
|
||||
public record MediaItemSearchResultViewModel(
|
||||
int Id,
|
||||
string Source,
|
||||
string MediaType,
|
||||
string Title,
|
||||
string Duration);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Queries
|
||||
{
|
||||
public record GetAggregateMediaItems
|
||||
(MediaType MediaType, string SearchString) : IRequest<List<AggregateMediaItemViewModel>>;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Queries
|
||||
{
|
||||
public class
|
||||
GetAggregateMediaItemsHandler : IRequestHandler<GetAggregateMediaItems, List<AggregateMediaItemViewModel>>
|
||||
{
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public GetAggregateMediaItemsHandler(IMediaItemRepository mediaItemRepository) =>
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
|
||||
public async Task<List<AggregateMediaItemViewModel>> Handle(
|
||||
GetAggregateMediaItems request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
IEnumerable<MediaItem> allItems = await _mediaItemRepository.GetAll(request.MediaType);
|
||||
|
||||
if (!string.IsNullOrEmpty(request.SearchString))
|
||||
{
|
||||
allItems = allItems.Filter(
|
||||
i => i.Metadata?.Title.Contains(request.SearchString, StringComparison.OrdinalIgnoreCase) ==
|
||||
true);
|
||||
}
|
||||
|
||||
return allItems.GroupBy(c => new { c.Source.Name, c.Metadata.Title }).Map(
|
||||
group => new AggregateMediaItemViewModel(
|
||||
group.Key.Name,
|
||||
group.Key.Title,
|
||||
group.Count(),
|
||||
group.Count() == 1 ? DisplayDuration(group.Head()) : string.Empty))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static string DisplayDuration(MediaItem mediaItem) => string.Format(
|
||||
mediaItem.Metadata?.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}",
|
||||
mediaItem.Metadata?.Duration);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.MediaItems.Mapper;
|
||||
|
||||
@@ -15,9 +16,9 @@ namespace ErsatzTV.Application.MediaItems.Queries
|
||||
public GetAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) =>
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
|
||||
public async Task<List<MediaItemViewModel>> Handle(
|
||||
public Task<List<MediaItemViewModel>> Handle(
|
||||
GetAllMediaItems request,
|
||||
CancellationToken cancellationToken) =>
|
||||
(await _mediaItemRepository.GetAll()).Map(ProjectToViewModel).ToList();
|
||||
_mediaItemRepository.GetAll().Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Queries
|
||||
{
|
||||
public record SearchAllMediaItems(string SearchString) : IRequest<List<MediaItemSearchResultViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
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.MediaItems.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Queries
|
||||
{
|
||||
public class SearchAllMediaItemsHandler : IRequestHandler<SearchAllMediaItems, List<MediaItemSearchResultViewModel>>
|
||||
{
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public SearchAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) =>
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
|
||||
public Task<List<MediaItemSearchResultViewModel>>
|
||||
Handle(SearchAllMediaItems request, CancellationToken cancellationToken) =>
|
||||
_mediaItemRepository.Search(request.SearchString).Map(list => list.Map(ProjectToSearchViewModel).ToList());
|
||||
}
|
||||
}
|
||||
@@ -41,28 +41,28 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
||||
Folder = folder
|
||||
});
|
||||
|
||||
private async Task<Validation<BaseError, string>> ValidateName(CreateLocalMediaSource createCollection)
|
||||
private async Task<Validation<BaseError, string>> ValidateName(CreateLocalMediaSource request)
|
||||
{
|
||||
List<string> allNames = await _mediaSourceRepository.GetAll()
|
||||
.Map(list => list.Map(c => c.Name).ToList());
|
||||
|
||||
Validation<BaseError, string> result1 = createCollection.NotEmpty(c => c.Name)
|
||||
.Bind(_ => createCollection.NotLongerThan(50)(c => c.Name));
|
||||
Validation<BaseError, string> result1 = request.NotEmpty(c => c.Name)
|
||||
.Bind(_ => request.NotLongerThan(50)(c => c.Name));
|
||||
|
||||
var result2 = Optional(createCollection.Name)
|
||||
var result2 = Optional(request.Name)
|
||||
.Filter(name => !allNames.Contains(name))
|
||||
.ToValidation<BaseError>("Media source name must be unique");
|
||||
|
||||
return (result1, result2).Apply((_, _) => createCollection.Name);
|
||||
return (result1, result2).Apply((_, _) => request.Name);
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, string>> ValidateFolder(CreateLocalMediaSource createCollection)
|
||||
private async Task<Validation<BaseError, string>> ValidateFolder(CreateLocalMediaSource request)
|
||||
{
|
||||
List<string> allFolders = await _mediaSourceRepository.GetAll()
|
||||
.Map(list => list.OfType<LocalMediaSource>().Map(c => c.Folder).ToList());
|
||||
|
||||
|
||||
return Optional(createCollection.Folder)
|
||||
return Optional(request.Folder)
|
||||
.Filter(folder => allFolders.ForAll(f => !AreSubPaths(f, folder)))
|
||||
.ToValidation<BaseError>("Folder must not belong to another media source");
|
||||
}
|
||||
|
||||
@@ -12,16 +12,10 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
||||
public class
|
||||
DeleteLocalMediaSourceHandler : IRequestHandler<DeleteLocalMediaSource, Either<BaseError, Task>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
|
||||
public DeleteLocalMediaSourceHandler(
|
||||
IMediaSourceRepository mediaSourceRepository,
|
||||
IMediaCollectionRepository mediaCollectionRepository)
|
||||
{
|
||||
public DeleteLocalMediaSourceHandler(IMediaSourceRepository mediaSourceRepository) =>
|
||||
_mediaSourceRepository = mediaSourceRepository;
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, Task>> Handle(
|
||||
DeleteLocalMediaSource request,
|
||||
@@ -30,14 +24,8 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
||||
.Map(DoDeletion)
|
||||
.ToEither<Task>();
|
||||
|
||||
private async Task DoDeletion(LocalMediaSource mediaSource)
|
||||
{
|
||||
private async Task DoDeletion(LocalMediaSource mediaSource) =>
|
||||
await _mediaSourceRepository.Delete(mediaSource.Id);
|
||||
if (mediaSource.MediaType == MediaType.TvShow)
|
||||
{
|
||||
await _mediaCollectionRepository.DeleteEmptyTelevisionCollections();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, LocalMediaSource>> MediaSourceMustExist(
|
||||
DeleteLocalMediaSource deleteMediaSource) =>
|
||||
|
||||
@@ -3,37 +3,60 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Locking;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using Unit = LanguageExt.Unit;
|
||||
|
||||
namespace ErsatzTV.Application.MediaSources.Commands
|
||||
{
|
||||
public class ScanLocalMediaSourceHandler : IRequestHandler<ScanLocalMediaSource, Either<BaseError, string>>
|
||||
{
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ILocalMediaScanner _localMediaScanner;
|
||||
private readonly IEntityLocker _entityLocker;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IMovieFolderScanner _movieFolderScanner;
|
||||
private readonly ITelevisionFolderScanner _televisionFolderScanner;
|
||||
|
||||
public ScanLocalMediaSourceHandler(
|
||||
IMediaSourceRepository mediaSourceRepository,
|
||||
IConfigElementRepository configElementRepository,
|
||||
ILocalMediaScanner localMediaScanner)
|
||||
IMovieFolderScanner movieFolderScanner,
|
||||
ITelevisionFolderScanner televisionFolderScanner,
|
||||
IEntityLocker entityLocker)
|
||||
{
|
||||
_mediaSourceRepository = mediaSourceRepository;
|
||||
_configElementRepository = configElementRepository;
|
||||
_localMediaScanner = localMediaScanner;
|
||||
_movieFolderScanner = movieFolderScanner;
|
||||
_televisionFolderScanner = televisionFolderScanner;
|
||||
_entityLocker = entityLocker;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, string>>
|
||||
Handle(ScanLocalMediaSource request, CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(
|
||||
p => _localMediaScanner.ScanLocalMediaSource(p.LocalMediaSource, p.FFprobePath)
|
||||
.Map(_ => p.LocalMediaSource.Name))
|
||||
.MapT(parameters => PerformScan(parameters).Map(_ => parameters.LocalMediaSource.Folder))
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<Unit> PerformScan(RequestParameters parameters)
|
||||
{
|
||||
switch (parameters.LocalMediaSource.MediaType)
|
||||
{
|
||||
case MediaType.Movie:
|
||||
await _movieFolderScanner.ScanFolder(parameters.LocalMediaSource, parameters.FFprobePath);
|
||||
break;
|
||||
case MediaType.TvShow:
|
||||
await _televisionFolderScanner.ScanFolder(parameters.LocalMediaSource, parameters.FFprobePath);
|
||||
break;
|
||||
}
|
||||
|
||||
_entityLocker.UnlockMediaSource(parameters.LocalMediaSource.Id);
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, RequestParameters>> Validate(ScanLocalMediaSource request) =>
|
||||
(await LocalMediaSourceMustExist(request), await ValidateFFprobePath())
|
||||
.Apply((localMediaSource, ffprobePath) => new RequestParameters(localMediaSource, ffprobePath));
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
||||
error =>
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Unable to synchronize libraries from Plex server {PlexServer}: {Error}",
|
||||
"Unable to synchronize libraries from plex server {PlexServer}: {Error}",
|
||||
connectionParameters.PlexMediaSource.Name,
|
||||
error.Value);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
@@ -10,7 +11,8 @@ namespace ErsatzTV.Application.MediaSources
|
||||
mediaSource switch
|
||||
{
|
||||
LocalMediaSource lms => new LocalMediaSourceViewModel(lms.Id, lms.Name, lms.Folder),
|
||||
PlexMediaSource pms => ProjectToViewModel(pms)
|
||||
PlexMediaSource pms => ProjectToViewModel(pms),
|
||||
_ => throw new NotSupportedException($"Unsupported media source {mediaSource.GetType().Name}")
|
||||
};
|
||||
|
||||
internal static PlexMediaSourceViewModel ProjectToViewModel(PlexMediaSource plexMediaSource) =>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.Movies
|
||||
{
|
||||
internal static class Mapper
|
||||
{
|
||||
internal static MovieViewModel ProjectToViewModel(MovieMediaItem movie) =>
|
||||
new(movie.Metadata.Title, movie.Metadata.Year?.ToString(), movie.Metadata.Plot, movie.Poster);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace ErsatzTV.Application.Movies
|
||||
{
|
||||
public record MovieViewModel(string Title, string Year, string Plot, string Poster);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Movies.Queries
|
||||
{
|
||||
public record GetMovieById(int Id) : IRequest<Option<MovieViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.Movies.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Movies.Queries
|
||||
{
|
||||
public class GetMovieByIdHandler : IRequestHandler<GetMovieById, Option<MovieViewModel>>
|
||||
{
|
||||
private readonly IMovieRepository _movieRepository;
|
||||
|
||||
public GetMovieByIdHandler(IMovieRepository movieRepository) =>
|
||||
_movieRepository = movieRepository;
|
||||
|
||||
public Task<Option<MovieViewModel>> Handle(
|
||||
GetMovieById request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_movieRepository.GetMovie(request.Id).MapT(ProjectToViewModel);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
@@ -62,8 +63,14 @@ namespace ErsatzTV.Application.Playouts.Commands
|
||||
|
||||
private async Task<Validation<BaseError, ProgramSchedule>> ProgramScheduleMustExist(
|
||||
CreatePlayout createPlayout) =>
|
||||
(await _programScheduleRepository.Get(createPlayout.ProgramScheduleId))
|
||||
.ToValidation<BaseError>("ProgramSchedule does not exist.");
|
||||
(await _programScheduleRepository.GetWithPlayouts(createPlayout.ProgramScheduleId))
|
||||
.ToValidation<BaseError>("ProgramSchedule does not exist.")
|
||||
.Bind(ProgramScheduleMustHaveItems);
|
||||
|
||||
private Validation<BaseError, ProgramSchedule> ProgramScheduleMustHaveItems(ProgramSchedule programSchedule) =>
|
||||
Optional(programSchedule)
|
||||
.Filter(ps => ps.Items.Any())
|
||||
.ToValidation<BaseError>("Program schedule must have items");
|
||||
|
||||
private Validation<BaseError, ProgramSchedulePlayoutType> ValidatePlayoutType(CreatePlayout createPlayout) =>
|
||||
Optional(createPlayout.ProgramSchedulePlayoutType)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using static LanguageExt.Prelude;
|
||||
using System.IO;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.Playouts
|
||||
{
|
||||
@@ -22,15 +22,18 @@ namespace ErsatzTV.Application.Playouts
|
||||
new(programSchedule.Id, programSchedule.Name);
|
||||
|
||||
private static string GetDisplayTitle(MediaItem mediaItem) =>
|
||||
mediaItem.Metadata.MediaType == MediaType.TvShow &&
|
||||
Optional(mediaItem.Metadata.SeasonNumber).IsSome &&
|
||||
Optional(mediaItem.Metadata.EpisodeNumber).IsSome
|
||||
? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}"
|
||||
: mediaItem.Metadata.Title;
|
||||
mediaItem switch
|
||||
{
|
||||
TelevisionEpisodeMediaItem e => e.Metadata != null
|
||||
? $"{e.Metadata.Title} - s{e.Metadata.Season:00}e{e.Metadata.Episode:00}"
|
||||
: Path.GetFileName(e.Path),
|
||||
MovieMediaItem m => m.Metadata?.Title ?? Path.GetFileName(m.Path),
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
public static string GetDisplayDuration(MediaItem mediaItem) =>
|
||||
private static string GetDisplayDuration(MediaItem mediaItem) =>
|
||||
string.Format(
|
||||
mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}",
|
||||
mediaItem.Metadata.Duration);
|
||||
mediaItem.Statistics.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}",
|
||||
mediaItem.Statistics.Duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands
|
||||
StartType StartType,
|
||||
TimeSpan? StartTime,
|
||||
PlayoutMode PlayoutMode,
|
||||
int MediaCollectionId,
|
||||
ProgramScheduleItemCollectionType CollectionType,
|
||||
int? MediaCollectionId,
|
||||
int? TelevisionShowId,
|
||||
int? TelevisionSeasonId,
|
||||
int? MultipleCount,
|
||||
TimeSpan? PlayoutDuration,
|
||||
bool? OfflineTail) : IRequest<Either<BaseError, ProgramScheduleItemViewModel>>, IProgramScheduleItemRequest;
|
||||
|
||||
@@ -6,7 +6,10 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands
|
||||
public interface IProgramScheduleItemRequest
|
||||
{
|
||||
TimeSpan? StartTime { get; }
|
||||
int MediaCollectionId { get; }
|
||||
ProgramScheduleItemCollectionType CollectionType { get; }
|
||||
int? MediaCollectionId { get; }
|
||||
int? TelevisionShowId { get; }
|
||||
int? TelevisionSeasonId { get; }
|
||||
PlayoutMode PlayoutMode { get; }
|
||||
int? MultipleCount { get; }
|
||||
TimeSpan? PlayoutDuration { get; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
@@ -52,6 +53,40 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands
|
||||
return programSchedule;
|
||||
}
|
||||
|
||||
protected Validation<BaseError, ProgramSchedule> CollectionTypeMustBeValid(
|
||||
IProgramScheduleItemRequest item,
|
||||
ProgramSchedule programSchedule)
|
||||
{
|
||||
switch (item.CollectionType)
|
||||
{
|
||||
case ProgramScheduleItemCollectionType.Collection:
|
||||
if (item.MediaCollectionId is null)
|
||||
{
|
||||
return BaseError.New("[MediaCollection] is required for collection type 'Collection'");
|
||||
}
|
||||
|
||||
break;
|
||||
case ProgramScheduleItemCollectionType.TelevisionShow:
|
||||
if (item.TelevisionShowId is null)
|
||||
{
|
||||
return BaseError.New("[TelevisionShow] is required for collection type 'TelevisionShow'");
|
||||
}
|
||||
|
||||
break;
|
||||
case ProgramScheduleItemCollectionType.TelevisionSeason:
|
||||
if (item.TelevisionSeasonId is null)
|
||||
{
|
||||
return BaseError.New("[TelevisionSeason] is required for collection type 'TelevisionSeason'");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return BaseError.New("[CollectionType] is invalid");
|
||||
}
|
||||
|
||||
return programSchedule;
|
||||
}
|
||||
|
||||
protected ProgramScheduleItem BuildItem(
|
||||
ProgramSchedule programSchedule,
|
||||
int index,
|
||||
@@ -63,21 +98,30 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands
|
||||
ProgramScheduleId = programSchedule.Id,
|
||||
Index = index,
|
||||
StartTime = item.StartTime,
|
||||
MediaCollectionId = item.MediaCollectionId
|
||||
CollectionType = item.CollectionType,
|
||||
MediaCollectionId = item.MediaCollectionId,
|
||||
TelevisionShowId = item.TelevisionShowId,
|
||||
TelevisionSeasonId = item.TelevisionSeasonId
|
||||
},
|
||||
PlayoutMode.One => new ProgramScheduleItemOne
|
||||
{
|
||||
ProgramScheduleId = programSchedule.Id,
|
||||
Index = index,
|
||||
StartTime = item.StartTime,
|
||||
MediaCollectionId = item.MediaCollectionId
|
||||
CollectionType = item.CollectionType,
|
||||
MediaCollectionId = item.MediaCollectionId,
|
||||
TelevisionShowId = item.TelevisionShowId,
|
||||
TelevisionSeasonId = item.TelevisionSeasonId
|
||||
},
|
||||
PlayoutMode.Multiple => new ProgramScheduleItemMultiple
|
||||
{
|
||||
ProgramScheduleId = programSchedule.Id,
|
||||
Index = index,
|
||||
StartTime = item.StartTime,
|
||||
CollectionType = item.CollectionType,
|
||||
MediaCollectionId = item.MediaCollectionId,
|
||||
TelevisionShowId = item.TelevisionShowId,
|
||||
TelevisionSeasonId = item.TelevisionSeasonId,
|
||||
Count = item.MultipleCount.GetValueOrDefault()
|
||||
},
|
||||
PlayoutMode.Duration => new ProgramScheduleItemDuration
|
||||
@@ -85,10 +129,14 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands
|
||||
ProgramScheduleId = programSchedule.Id,
|
||||
Index = index,
|
||||
StartTime = item.StartTime,
|
||||
CollectionType = item.CollectionType,
|
||||
MediaCollectionId = item.MediaCollectionId,
|
||||
TelevisionShowId = item.TelevisionShowId,
|
||||
TelevisionSeasonId = item.TelevisionSeasonId,
|
||||
PlayoutDuration = item.PlayoutDuration.GetValueOrDefault(),
|
||||
OfflineTail = item.OfflineTail.GetValueOrDefault()
|
||||
}
|
||||
},
|
||||
_ => throw new NotSupportedException($"Unsupported playout mode {item.PlayoutMode}")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands
|
||||
StartType StartType,
|
||||
TimeSpan? StartTime,
|
||||
PlayoutMode PlayoutMode,
|
||||
int MediaCollectionId,
|
||||
ProgramScheduleItemCollectionType CollectionType,
|
||||
int? MediaCollectionId,
|
||||
int? TelevisionShowId,
|
||||
int? TelevisionSeasonId,
|
||||
int? MultipleCount,
|
||||
TimeSpan? PlayoutDuration,
|
||||
bool? OfflineTail) : IProgramScheduleItemRequest;
|
||||
|
||||
+8
-1
@@ -55,12 +55,19 @@ namespace ErsatzTV.Application.ProgramSchedules.Commands
|
||||
|
||||
private Task<Validation<BaseError, ProgramSchedule>> Validate(ReplaceProgramScheduleItems request) =>
|
||||
ProgramScheduleMustExist(request.ProgramScheduleId)
|
||||
.BindT(programSchedule => PlayoutModesMustBeValid(request, programSchedule));
|
||||
.BindT(programSchedule => PlayoutModesMustBeValid(request, programSchedule))
|
||||
.BindT(programSchedule => CollectionTypesMustBeValid(request, programSchedule));
|
||||
|
||||
private Validation<BaseError, ProgramSchedule> PlayoutModesMustBeValid(
|
||||
ReplaceProgramScheduleItems request,
|
||||
ProgramSchedule programSchedule) =>
|
||||
request.Items.Map(item => PlayoutModeMustBeValid(item, programSchedule)).Sequence()
|
||||
.Map(_ => programSchedule);
|
||||
|
||||
private Validation<BaseError, ProgramSchedule> CollectionTypesMustBeValid(
|
||||
ReplaceProgramScheduleItems request,
|
||||
ProgramSchedule programSchedule) =>
|
||||
request.Items.Map(item => CollectionTypeMustBeValid(item, programSchedule)).Sequence()
|
||||
.Map(_ => programSchedule);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using System;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules
|
||||
{
|
||||
@@ -16,7 +17,16 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
duration.Index,
|
||||
duration.StartType,
|
||||
duration.StartTime,
|
||||
MediaCollections.Mapper.ProjectToViewModel(duration.MediaCollection),
|
||||
duration.CollectionType,
|
||||
duration.MediaCollection != null
|
||||
? MediaCollections.Mapper.ProjectToViewModel(duration.MediaCollection)
|
||||
: null,
|
||||
duration.TelevisionShow != null
|
||||
? Television.Mapper.ProjectToViewModel(duration.TelevisionShow)
|
||||
: null,
|
||||
duration.TelevisionSeason != null
|
||||
? Television.Mapper.ProjectToViewModel(duration.TelevisionSeason)
|
||||
: null,
|
||||
duration.PlayoutDuration,
|
||||
duration.OfflineTail),
|
||||
ProgramScheduleItemFlood flood =>
|
||||
@@ -25,14 +35,32 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
flood.Index,
|
||||
flood.StartType,
|
||||
flood.StartTime,
|
||||
MediaCollections.Mapper.ProjectToViewModel(flood.MediaCollection)),
|
||||
flood.CollectionType,
|
||||
flood.MediaCollection != null
|
||||
? MediaCollections.Mapper.ProjectToViewModel(flood.MediaCollection)
|
||||
: null,
|
||||
flood.TelevisionShow != null
|
||||
? Television.Mapper.ProjectToViewModel(flood.TelevisionShow)
|
||||
: null,
|
||||
flood.TelevisionSeason != null
|
||||
? Television.Mapper.ProjectToViewModel(flood.TelevisionSeason)
|
||||
: null),
|
||||
ProgramScheduleItemMultiple multiple =>
|
||||
new ProgramScheduleItemMultipleViewModel(
|
||||
multiple.Id,
|
||||
multiple.Index,
|
||||
multiple.StartType,
|
||||
multiple.StartTime,
|
||||
MediaCollections.Mapper.ProjectToViewModel(multiple.MediaCollection),
|
||||
multiple.CollectionType,
|
||||
multiple.MediaCollection != null
|
||||
? MediaCollections.Mapper.ProjectToViewModel(multiple.MediaCollection)
|
||||
: null,
|
||||
multiple.TelevisionShow != null
|
||||
? Television.Mapper.ProjectToViewModel(multiple.TelevisionShow)
|
||||
: null,
|
||||
multiple.TelevisionSeason != null
|
||||
? Television.Mapper.ProjectToViewModel(multiple.TelevisionSeason)
|
||||
: null,
|
||||
multiple.Count),
|
||||
ProgramScheduleItemOne one =>
|
||||
new ProgramScheduleItemOneViewModel(
|
||||
@@ -40,7 +68,16 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
one.Index,
|
||||
one.StartType,
|
||||
one.StartTime,
|
||||
MediaCollections.Mapper.ProjectToViewModel(one.MediaCollection))
|
||||
one.CollectionType,
|
||||
one.MediaCollection != null
|
||||
? MediaCollections.Mapper.ProjectToViewModel(one.MediaCollection)
|
||||
: null,
|
||||
one.TelevisionShow != null ? Television.Mapper.ProjectToViewModel(one.TelevisionShow) : null,
|
||||
one.TelevisionSeason != null
|
||||
? Television.Mapper.ProjectToViewModel(one.TelevisionSeason)
|
||||
: null),
|
||||
_ => throw new NotSupportedException(
|
||||
$"Unsupported program schedule item type {programScheduleItem.GetType().Name}")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using ErsatzTV.Application.MediaCollections;
|
||||
using ErsatzTV.Application.Television;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules
|
||||
@@ -11,7 +12,10 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
int index,
|
||||
StartType startType,
|
||||
TimeSpan? startTime,
|
||||
ProgramScheduleItemCollectionType collectionType,
|
||||
MediaCollectionViewModel mediaCollection,
|
||||
TelevisionShowViewModel televisionShow,
|
||||
TelevisionSeasonViewModel televisionSeason,
|
||||
TimeSpan playoutDuration,
|
||||
bool offlineTail) : base(
|
||||
id,
|
||||
@@ -19,7 +23,10 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
startType,
|
||||
startTime,
|
||||
PlayoutMode.Duration,
|
||||
mediaCollection)
|
||||
collectionType,
|
||||
mediaCollection,
|
||||
televisionShow,
|
||||
televisionSeason)
|
||||
{
|
||||
PlayoutDuration = playoutDuration;
|
||||
OfflineTail = offlineTail;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using ErsatzTV.Application.MediaCollections;
|
||||
using ErsatzTV.Application.Television;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules
|
||||
@@ -11,13 +12,19 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
int index,
|
||||
StartType startType,
|
||||
TimeSpan? startTime,
|
||||
MediaCollectionViewModel mediaCollection) : base(
|
||||
ProgramScheduleItemCollectionType collectionType,
|
||||
MediaCollectionViewModel mediaCollection,
|
||||
TelevisionShowViewModel televisionShow,
|
||||
TelevisionSeasonViewModel televisionSeason) : base(
|
||||
id,
|
||||
index,
|
||||
startType,
|
||||
startTime,
|
||||
PlayoutMode.Flood,
|
||||
mediaCollection)
|
||||
collectionType,
|
||||
mediaCollection,
|
||||
televisionShow,
|
||||
televisionSeason)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using ErsatzTV.Application.MediaCollections;
|
||||
using ErsatzTV.Application.Television;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules
|
||||
@@ -11,14 +12,20 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
int index,
|
||||
StartType startType,
|
||||
TimeSpan? startTime,
|
||||
ProgramScheduleItemCollectionType collectionType,
|
||||
MediaCollectionViewModel mediaCollection,
|
||||
TelevisionShowViewModel televisionShow,
|
||||
TelevisionSeasonViewModel televisionSeason,
|
||||
int count) : base(
|
||||
id,
|
||||
index,
|
||||
startType,
|
||||
startTime,
|
||||
PlayoutMode.Multiple,
|
||||
mediaCollection) =>
|
||||
collectionType,
|
||||
mediaCollection,
|
||||
televisionShow,
|
||||
televisionSeason) =>
|
||||
Count = count;
|
||||
|
||||
public int Count { get; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using ErsatzTV.Application.MediaCollections;
|
||||
using ErsatzTV.Application.Television;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules
|
||||
@@ -11,13 +12,19 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
int index,
|
||||
StartType startType,
|
||||
TimeSpan? startTime,
|
||||
MediaCollectionViewModel mediaCollection) : base(
|
||||
ProgramScheduleItemCollectionType collectionType,
|
||||
MediaCollectionViewModel mediaCollection,
|
||||
TelevisionShowViewModel televisionShow,
|
||||
TelevisionSeasonViewModel televisionSeason) : base(
|
||||
id,
|
||||
index,
|
||||
startType,
|
||||
startTime,
|
||||
PlayoutMode.One,
|
||||
mediaCollection)
|
||||
collectionType,
|
||||
mediaCollection,
|
||||
televisionShow,
|
||||
televisionSeason)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using ErsatzTV.Application.MediaCollections;
|
||||
using ErsatzTV.Application.Television;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.ProgramSchedules
|
||||
@@ -10,5 +11,18 @@ namespace ErsatzTV.Application.ProgramSchedules
|
||||
StartType StartType,
|
||||
TimeSpan? StartTime,
|
||||
PlayoutMode PlayoutMode,
|
||||
MediaCollectionViewModel MediaCollection);
|
||||
ProgramScheduleItemCollectionType CollectionType,
|
||||
MediaCollectionViewModel MediaCollection,
|
||||
TelevisionShowViewModel TelevisionShow,
|
||||
TelevisionSeasonViewModel TelevisionSeason)
|
||||
{
|
||||
public string Name => CollectionType switch
|
||||
{
|
||||
ProgramScheduleItemCollectionType.Collection => MediaCollection?.Name,
|
||||
ProgramScheduleItemCollectionType.TelevisionShow => $"{TelevisionShow?.Title} ({TelevisionShow?.Year})",
|
||||
ProgramScheduleItemCollectionType.TelevisionSeason =>
|
||||
$"{TelevisionSeason?.Title} ({TelevisionSeason?.Plot})",
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.Television
|
||||
{
|
||||
internal static class Mapper
|
||||
{
|
||||
internal static TelevisionShowViewModel ProjectToViewModel(TelevisionShow show) =>
|
||||
new(show.Id, show.Metadata.Title, show.Metadata.Year?.ToString(), show.Metadata.Plot, show.Poster);
|
||||
|
||||
internal static TelevisionSeasonViewModel ProjectToViewModel(TelevisionSeason season) =>
|
||||
new(
|
||||
season.Id,
|
||||
season.TelevisionShowId,
|
||||
season.TelevisionShow.Metadata.Title,
|
||||
season.TelevisionShow.Metadata.Year?.ToString(),
|
||||
season.Number == 0 ? "Specials" : $"Season {season.Number}",
|
||||
season.Poster);
|
||||
|
||||
internal static TelevisionEpisodeViewModel ProjectToViewModel(TelevisionEpisodeMediaItem episode) =>
|
||||
new(
|
||||
episode.Season.TelevisionShowId,
|
||||
episode.SeasonId,
|
||||
episode.Metadata.Episode,
|
||||
episode.Metadata.Title,
|
||||
episode.Metadata.Plot,
|
||||
episode.Poster);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public record GetAllTelevisionSeasons : IRequest<List<TelevisionSeasonViewModel>>;
|
||||
}
|
||||
@@ -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.Television.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public class
|
||||
GetAllTelevisionSeasonsHandler : IRequestHandler<GetAllTelevisionSeasons, List<TelevisionSeasonViewModel>>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetAllTelevisionSeasonsHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public Task<List<TelevisionSeasonViewModel>> Handle(
|
||||
GetAllTelevisionSeasons request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_televisionRepository.GetAllSeasons().Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public record GetAllTelevisionShows : IRequest<List<TelevisionShowViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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.Television.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public class GetAllTelevisionShowsHandler : IRequestHandler<GetAllTelevisionShows, List<TelevisionShowViewModel>>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetAllTelevisionShowsHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public Task<List<TelevisionShowViewModel>> Handle(
|
||||
GetAllTelevisionShows request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_televisionRepository.GetAllShows().Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public record GetTelevisionEpisodeById(int EpisodeId) : IRequest<Option<TelevisionEpisodeViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.Television.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public class
|
||||
GetTelevisionEpisodeByIdHandler : IRequestHandler<GetTelevisionEpisodeById, Option<TelevisionEpisodeViewModel>>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetTelevisionEpisodeByIdHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public Task<Option<TelevisionEpisodeViewModel>> Handle(
|
||||
GetTelevisionEpisodeById request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_televisionRepository.GetEpisode(request.EpisodeId)
|
||||
.MapT(ProjectToViewModel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public record GetTelevisionSeasonById(int SeasonId) : IRequest<Option<TelevisionSeasonViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.Television.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public class
|
||||
GetTelevisionSeasonByIdHandler : IRequestHandler<GetTelevisionSeasonById, Option<TelevisionSeasonViewModel>>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetTelevisionSeasonByIdHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public Task<Option<TelevisionSeasonViewModel>> Handle(
|
||||
GetTelevisionSeasonById request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_televisionRepository.GetSeason(request.SeasonId)
|
||||
.MapT(ProjectToViewModel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public record GetTelevisionShowById(int Id) : IRequest<Option<TelevisionShowViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.Television.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Television.Queries
|
||||
{
|
||||
public class GetTelevisionShowByIdHandler : IRequestHandler<GetTelevisionShowById, Option<TelevisionShowViewModel>>
|
||||
{
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public GetTelevisionShowByIdHandler(ITelevisionRepository televisionRepository) =>
|
||||
_televisionRepository = televisionRepository;
|
||||
|
||||
public Task<Option<TelevisionShowViewModel>> Handle(
|
||||
GetTelevisionShowById request,
|
||||
CancellationToken cancellationToken) =>
|
||||
_televisionRepository.GetShow(request.Id)
|
||||
.MapT(ProjectToViewModel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace ErsatzTV.Application.Television
|
||||
{
|
||||
public record TelevisionEpisodeViewModel(
|
||||
int ShowId,
|
||||
int SeasonId,
|
||||
int Episode,
|
||||
string Title,
|
||||
string Plot,
|
||||
string Poster);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user