Browse Source

adjust block unique constraint (#1634)

* upgrade dependencies

* allow blocks with same name in different groups

* code cleanup
pull/1636/head
Jason Dove 2 years ago committed by GitHub
parent
commit
087901d177
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .config/dotnet-tools.json
  2. 3
      ErsatzTV.Application/Channels/Commands/CreateChannel.cs
  3. 2
      ErsatzTV.Application/Channels/Commands/DeleteChannelHandler.cs
  4. 112
      ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs
  5. 10
      ErsatzTV.Application/Channels/Commands/RefreshChannelListHandler.cs
  6. 3
      ErsatzTV.Application/Channels/Commands/UpdateChannel.cs
  7. 4
      ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs
  8. 4
      ErsatzTV.Application/Channels/Queries/GetChannelGuide.cs
  9. 18
      ErsatzTV.Application/Configuration/Commands/UpdateGeneralSettingsHandler.cs
  10. 2
      ErsatzTV.Application/Configuration/Commands/UpdateXmltvSettingsHandler.cs
  11. 6
      ErsatzTV.Application/Configuration/Queries/GetGeneralSettingsHandler.cs
  12. 3
      ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferences.cs
  13. 2
      ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferencesHandler.cs
  14. 10
      ErsatzTV.Application/Emby/EmbyLibraryViewModel.cs
  15. 3
      ErsatzTV.Application/Emby/Queries/GetEmbyPathReplacementsBySourceId.cs
  16. 4
      ErsatzTV.Application/ErsatzTV.Application.csproj
  17. 3
      ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfile.cs
  18. 4
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs
  19. 4
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs
  20. 2
      ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdForApiHandler.cs
  21. 2
      ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs
  22. 12
      ErsatzTV.Application/Images/Commands/UpdateImageFolderDurationHandler.cs
  23. 5
      ErsatzTV.Application/Images/Queries/GetCachedImagePath.cs
  24. 2
      ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinAdminUserIdHandler.cs
  25. 4
      ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinLibraryById.cs
  26. 4
      ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferences.cs
  27. 2
      ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferencesHandler.cs
  28. 10
      ErsatzTV.Application/Jellyfin/JellyfinLibraryViewModel.cs
  29. 2
      ErsatzTV.Application/Jellyfin/Queries/GetAllJellyfinMediaSourcesHandler.cs
  30. 2
      ErsatzTV.Application/Jellyfin/Queries/GetJellyfinLibrariesBySourceIdHandler.cs
  31. 3
      ErsatzTV.Application/Jellyfin/Queries/GetJellyfinMediaSourceById.cs
  32. 2
      ErsatzTV.Application/Jellyfin/Queries/GetJellyfinMediaSourceByIdHandler.cs
  33. 4
      ErsatzTV.Application/Jellyfin/Queries/GetJellyfinPathReplacementsBySourceId.cs
  34. 11
      ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs
  35. 2
      ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryHandler.cs
  36. 4
      ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPath.cs
  37. 2
      ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryHandler.cs
  38. 2
      ErsatzTV.Application/Libraries/Commands/MoveLocalLibraryPathHandler.cs
  39. 2
      ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs
  40. 10
      ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs
  41. 3
      ErsatzTV.Application/MediaCards/ArtistCardViewModel.cs
  42. 3
      ErsatzTV.Application/MediaCards/ImageCardViewModel.cs
  43. 3
      ErsatzTV.Application/MediaCards/MovieCardViewModel.cs
  44. 3
      ErsatzTV.Application/MediaCards/MusicVideoCardViewModel.cs
  45. 3
      ErsatzTV.Application/MediaCards/OtherVideoCardViewModel.cs
  46. 3
      ErsatzTV.Application/MediaCards/Queries/GetMusicVideoCards.cs
  47. 4
      ErsatzTV.Application/MediaCards/Queries/GetTelevisionEpisodeCards.cs
  48. 4
      ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCards.cs
  49. 2
      ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCardsHandler.cs
  50. 3
      ErsatzTV.Application/MediaCards/SongCardViewModel.cs
  51. 3
      ErsatzTV.Application/MediaCards/TelevisionEpisodeCardViewModel.cs
  52. 3
      ErsatzTV.Application/MediaCards/TelevisionSeasonCardViewModel.cs
  53. 3
      ErsatzTV.Application/MediaCards/TelevisionShowCardViewModel.cs
  54. 3
      ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollection.cs
  55. 2
      ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollectionHandler.cs
  56. 4
      ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs
  57. 3
      ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollection.cs
  58. 2
      ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs
  59. 3
      ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollection.cs
  60. 4
      ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs
  61. 3
      ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollection.cs
  62. 4
      ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs
  63. 2
      ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs
  64. 2
      ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs
  65. 3
      ErsatzTV.Application/MediaCollections/Commands/AddSongToCollection.cs
  66. 2
      ErsatzTV.Application/MediaCollections/Commands/AddSongToCollectionHandler.cs
  67. 4
      ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollection.cs
  68. 3
      ErsatzTV.Application/MediaCollections/Commands/CreateSmartCollection.cs
  69. 2
      ErsatzTV.Application/MediaCollections/Commands/DeleteTraktListHandler.cs
  70. 2
      ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs
  71. 3
      ErsatzTV.Application/MediaCollections/Commands/UpdateCollection.cs
  72. 3
      ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionCustomOrder.cs
  73. 2
      ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionCustomOrderHandler.cs
  74. 4
      ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs
  75. 3
      ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollection.cs
  76. 6
      ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs
  77. 2
      ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs
  78. 7
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  79. 4
      ErsatzTV.Application/Playouts/Commands/CreateExternalJsonPlayoutHandler.cs
  80. 4
      ErsatzTV.Application/Playouts/Commands/CreateFloodPlayoutHandler.cs
  81. 4
      ErsatzTV.Application/Playouts/Commands/ReplacePlayoutAlternateScheduleItems.cs
  82. 8
      ErsatzTV.Application/Playouts/Commands/UpdateExternalJsonPlayoutHandler.cs
  83. 4
      ErsatzTV.Application/Playouts/Commands/UpdatePlayout.cs
  84. 4
      ErsatzTV.Application/Playouts/Queries/GetFuturePlayoutItemsById.cs
  85. 3
      ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferences.cs
  86. 2
      ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs
  87. 3
      ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacements.cs
  88. 4
      ErsatzTV.Application/Plex/Queries/GetPlexConnectionParameters.cs
  89. 3
      ErsatzTV.Application/Plex/Queries/GetPlexPathReplacementsBySourceId.cs
  90. 2
      ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItemHandler.cs
  91. 4
      ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramSchedule.cs
  92. 5
      ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs
  93. 3
      ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramSchedule.cs
  94. 2
      ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramScheduleHandler.cs
  95. 8
      ErsatzTV.Application/Scheduling/Commands/CreateBlockGroupHandler.cs
  96. 39
      ErsatzTV.Application/Scheduling/Commands/CreateBlockHandler.cs
  97. 2
      ErsatzTV.Application/Scheduling/Commands/EraseBlockPlayoutItemsHandler.cs
  98. 6
      ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItemsHandler.cs
  99. 4
      ErsatzTV.Application/Scheduling/Commands/ReplacePlayoutTemplateItemsHandler.cs
  100. 3
      ErsatzTV.Application/Scheduling/Queries/GetTemplateItemsHandler.cs
  101. Some files were not shown because too many files have changed in this diff Show More

2
.config/dotnet-tools.json

@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"jetbrains.resharper.globaltools": { "jetbrains.resharper.globaltools": {
"version": "2023.2.0", "version": "2023.3.3",
"commands": [ "commands": [
"jb" "jb"
] ]

3
ErsatzTV.Application/Channels/Commands/CreateChannel.cs

@ -3,8 +3,7 @@ using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Channels; namespace ErsatzTV.Application.Channels;
public record CreateChannel public record CreateChannel(
(
string Name, string Name,
string Number, string Number,
string Group, string Group,

2
ErsatzTV.Application/Channels/Commands/DeleteChannelHandler.cs

@ -39,7 +39,7 @@ public class DeleteChannelHandler : IRequestHandler<DeleteChannel, Either<BaseEr
{ {
dbContext.Channels.Remove(channel); dbContext.Channels.Remove(channel);
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
_searchTargets.SearchTargetsChanged(); _searchTargets.SearchTargetsChanged();
// delete channel data from channel guide cache // delete channel data from channel guide cache

112
ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs

@ -22,9 +22,9 @@ namespace ErsatzTV.Application.Channels;
public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData> public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
private readonly IConfigElementRepository _configElementRepository;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ILocalFileSystem _localFileSystem; private readonly ILocalFileSystem _localFileSystem;
private readonly IConfigElementRepository _configElementRepository;
private readonly ILogger<RefreshChannelDataHandler> _logger; private readonly ILogger<RefreshChannelDataHandler> _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager; private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
@ -45,7 +45,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
public async Task Handle(RefreshChannelData request, CancellationToken cancellationToken) public async Task Handle(RefreshChannelData request, CancellationToken cancellationToken)
{ {
_logger.LogDebug("Refreshing channel data (XMLTV) for channel {Channel}", request.ChannelNumber); _logger.LogDebug("Refreshing channel data (XMLTV) for channel {Channel}", request.ChannelNumber);
_localFileSystem.EnsureFolderExists(FileSystemLayout.ChannelGuideCacheFolder); _localFileSystem.EnsureFolderExists(FileSystemLayout.ChannelGuideCacheFolder);
string movieTemplateFileName = GetMovieTemplateFileName(); string movieTemplateFileName = GetMovieTemplateFileName();
@ -68,7 +68,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
}); });
var templateContext = new XmlTemplateContext(); var templateContext = new XmlTemplateContext();
string movieText = await File.ReadAllTextAsync(movieTemplateFileName, cancellationToken); string movieText = await File.ReadAllTextAsync(movieTemplateFileName, cancellationToken);
var movieTemplate = Template.Parse(movieText, movieTemplateFileName); var movieTemplate = Template.Parse(movieText, movieTemplateFileName);
@ -302,7 +302,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
(_, true) => displayItem.GuideFinishOffset!.Value, (_, true) => displayItem.GuideFinishOffset!.Value,
(_, false) => finishItem.FinishOffset (_, false) => finishItem.FinishOffset
}; };
string start = startTime string start = startTime
.ToString("yyyyMMddHHmmss zzz", CultureInfo.InvariantCulture) .ToString("yyyyMMddHHmmss zzz", CultureInfo.InvariantCulture)
.Replace(":", string.Empty); .Replace(":", string.Empty);
@ -329,7 +329,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
i++; i++;
} }
} }
private static async Task WriteBlockPlayoutXml( private static async Task WriteBlockPlayoutXml(
RefreshChannelData request, RefreshChannelData request,
List<PlayoutItem> sorted, List<PlayoutItem> sorted,
@ -367,7 +367,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
item, item,
start, start,
stop, stop,
hasCustomTitle: false, false,
templateContext, templateContext,
movieTemplate, movieTemplate,
episodeTemplate, episodeTemplate,
@ -481,7 +481,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
metadata.Genres ??= []; metadata.Genres ??= [];
metadata.Guids ??= []; metadata.Guids ??= [];
string poster = Optional(metadata.Artwork).Flatten() string poster = Optional(metadata.Artwork).Flatten()
.Filter(a => a.ArtworkKind == ArtworkKind.Poster) .Filter(a => a.ArtworkKind == ArtworkKind.Poster)
.HeadOrNone() .HeadOrNone()
@ -491,9 +491,9 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
ProgrammeStart = start, ProgrammeStart = start,
ProgrammeStop = stop, ProgrammeStop = stop,
ChannelNumber = request.ChannelNumber, request.ChannelNumber,
HasCustomTitle = hasCustomTitle, HasCustomTitle = hasCustomTitle,
CustomTitle = displayItem.CustomTitle, displayItem.CustomTitle,
MovieTitle = title, MovieTitle = title,
MovieHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot), MovieHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot),
MoviePlot = metadata.Plot, MoviePlot = metadata.Plot,
@ -516,7 +516,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
return Option<string>.None; return Option<string>.None;
} }
private static async Task<Option<string>> ProcessEpisodeTemplate( private static async Task<Option<string>> ProcessEpisodeTemplate(
RefreshChannelData request, RefreshChannelData request,
Episode templateEpisode, Episode templateEpisode,
@ -546,9 +546,9 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
ProgrammeStart = start, ProgrammeStart = start,
ProgrammeStop = stop, ProgrammeStop = stop,
ChannelNumber = request.ChannelNumber, request.ChannelNumber,
HasCustomTitle = hasCustomTitle, HasCustomTitle = hasCustomTitle,
CustomTitle = displayItem.CustomTitle, displayItem.CustomTitle,
ShowTitle = title, ShowTitle = title,
EpisodeHasTitle = !string.IsNullOrWhiteSpace(subtitle), EpisodeHasTitle = !string.IsNullOrWhiteSpace(subtitle),
EpisodeTitle = subtitle, EpisodeTitle = subtitle,
@ -560,13 +560,13 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
EpisodeHasArtwork = !string.IsNullOrWhiteSpace(artworkPath), EpisodeHasArtwork = !string.IsNullOrWhiteSpace(artworkPath),
EpisodeArtworkUrl = artworkPath, EpisodeArtworkUrl = artworkPath,
SeasonNumber = templateEpisode.Season?.SeasonNumber ?? 0, SeasonNumber = templateEpisode.Season?.SeasonNumber ?? 0,
EpisodeNumber = metadata.EpisodeNumber, metadata.EpisodeNumber,
ShowHasContentRating = !string.IsNullOrWhiteSpace(showMetadata.ContentRating), ShowHasContentRating = !string.IsNullOrWhiteSpace(showMetadata.ContentRating),
ShowContentRating = showMetadata.ContentRating, ShowContentRating = showMetadata.ContentRating,
ShowGuids = showMetadata.Guids.Map(g => g.Guid), ShowGuids = showMetadata.Guids.Map(g => g.Guid),
EpisodeGuids = metadata.Guids.Map(g => g.Guid) EpisodeGuids = metadata.Guids.Map(g => g.Guid)
}; };
var scriptObject = new ScriptObject(); var scriptObject = new ScriptObject();
scriptObject.Import(data); scriptObject.Import(data);
templateContext.PushGlobal(scriptObject); templateContext.PushGlobal(scriptObject);
@ -606,9 +606,9 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
ProgrammeStart = start, ProgrammeStart = start,
ProgrammeStop = stop, ProgrammeStop = stop,
ChannelNumber = request.ChannelNumber, request.ChannelNumber,
HasCustomTitle = hasCustomTitle, HasCustomTitle = hasCustomTitle,
CustomTitle = displayItem.CustomTitle, displayItem.CustomTitle,
ArtistTitle = title, ArtistTitle = title,
MusicVideoTitle = subtitle, MusicVideoTitle = subtitle,
MusicVideoHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot), MusicVideoHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot),
@ -639,7 +639,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
return Option<string>.None; return Option<string>.None;
} }
private static async Task<Option<string>> ProcessSongTemplate( private static async Task<Option<string>> ProcessSongTemplate(
RefreshChannelData request, RefreshChannelData request,
Song templateSong, Song templateSong,
@ -663,9 +663,9 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
ProgrammeStart = start, ProgrammeStart = start,
ProgrammeStop = stop, ProgrammeStop = stop,
ChannelNumber = request.ChannelNumber, request.ChannelNumber,
HasCustomTitle = hasCustomTitle, HasCustomTitle = hasCustomTitle,
CustomTitle = displayItem.CustomTitle, displayItem.CustomTitle,
SongTitle = subtitle, SongTitle = subtitle,
SongArtists = metadata.Artists, SongArtists = metadata.Artists,
SongAlbumArtists = metadata.AlbumArtists, SongAlbumArtists = metadata.AlbumArtists,
@ -682,7 +682,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
SongAlbum = metadata.Album, SongAlbum = metadata.Album,
SongHasReleaseDate = metadata.ReleaseDate.HasValue, SongHasReleaseDate = metadata.ReleaseDate.HasValue,
SongReleaseDate = metadata.ReleaseDate, SongReleaseDate = metadata.ReleaseDate,
SongStudios = metadata.Studios.Map(s => s.Name), SongStudios = metadata.Studios.Map(s => s.Name)
}; };
var scriptObject = new ScriptObject(); var scriptObject = new ScriptObject();
@ -694,7 +694,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
return Option<string>.None; return Option<string>.None;
} }
private static async Task<Option<string>> ProcessOtherVideoTemplate( private static async Task<Option<string>> ProcessOtherVideoTemplate(
RefreshChannelData request, RefreshChannelData request,
OtherVideo templateOtherVideo, OtherVideo templateOtherVideo,
@ -710,14 +710,14 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
metadata.Genres ??= []; metadata.Genres ??= [];
metadata.Guids ??= []; metadata.Guids ??= [];
var data = new var data = new
{ {
ProgrammeStart = start, ProgrammeStart = start,
ProgrammeStop = stop, ProgrammeStop = stop,
ChannelNumber = request.ChannelNumber, request.ChannelNumber,
HasCustomTitle = hasCustomTitle, HasCustomTitle = hasCustomTitle,
CustomTitle = displayItem.CustomTitle, displayItem.CustomTitle,
OtherVideoTitle = title, OtherVideoTitle = title,
OtherVideoHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot), OtherVideoHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot),
OtherVideoPlot = metadata.Plot, OtherVideoPlot = metadata.Plot,
@ -741,13 +741,13 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
private string GetMovieTemplateFileName() private string GetMovieTemplateFileName()
{ {
string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "movie.sbntxt"); string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "movie.sbntxt");
// fall back to default template // fall back to default template
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_movie.sbntxt"); templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_movie.sbntxt");
} }
// fail if file doesn't exist // fail if file doesn't exist
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
@ -760,17 +760,17 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
return templateFileName; return templateFileName;
} }
private string GetEpisodeTemplateFileName() private string GetEpisodeTemplateFileName()
{ {
string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "episode.sbntxt"); string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "episode.sbntxt");
// fall back to default template // fall back to default template
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_episode.sbntxt"); templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_episode.sbntxt");
} }
// fail if file doesn't exist // fail if file doesn't exist
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
@ -783,17 +783,17 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
return templateFileName; return templateFileName;
} }
private string GetMusicVideoTemplateFileName() private string GetMusicVideoTemplateFileName()
{ {
string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "musicVideo.sbntxt"); string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "musicVideo.sbntxt");
// fall back to default template // fall back to default template
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_musicVideo.sbntxt"); templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_musicVideo.sbntxt");
} }
// fail if file doesn't exist // fail if file doesn't exist
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
@ -806,17 +806,17 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
return templateFileName; return templateFileName;
} }
private string GetSongTemplateFileName() private string GetSongTemplateFileName()
{ {
string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "song.sbntxt"); string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "song.sbntxt");
// fall back to default template // fall back to default template
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_song.sbntxt"); templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_song.sbntxt");
} }
// fail if file doesn't exist // fail if file doesn't exist
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
@ -829,17 +829,17 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
return templateFileName; return templateFileName;
} }
private string GetOtherVideoTemplateFileName() private string GetOtherVideoTemplateFileName()
{ {
string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "otherVideo.sbntxt"); string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "otherVideo.sbntxt");
// fall back to default template // fall back to default template
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_otherVideo.sbntxt"); templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_otherVideo.sbntxt");
} }
// fail if file doesn't exist // fail if file doesn't exist
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
@ -863,10 +863,12 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
_ => 440 _ => 440
}; };
if (artworkPath.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || artworkPath.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) if (artworkPath.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
artworkPath.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{ {
return artworkPath; return artworkPath;
} }
if (artworkPath.StartsWith("jellyfin://", StringComparison.OrdinalIgnoreCase)) if (artworkPath.StartsWith("jellyfin://", StringComparison.OrdinalIgnoreCase))
{ {
artworkPath = JellyfinUrl.PlaceholderProxyForArtwork(artworkPath, artworkKind, height); artworkPath = JellyfinUrl.PlaceholderProxyForArtwork(artworkPath, artworkKind, height);
@ -1025,7 +1027,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
foreach (ExternalJsonChannel channel in maybeChannel) foreach (ExternalJsonChannel channel in maybeChannel)
{ {
// TODO: null start time should log and throw // TODO: null start time should log and throw
DateTimeOffset startTime = DateTimeOffset.Parse( DateTimeOffset startTime = DateTimeOffset.Parse(
channel.StartTime ?? string.Empty, channel.StartTime ?? string.Empty,
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
@ -1077,14 +1079,15 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
var artwork = new List<Artwork>(); var artwork = new List<Artwork>();
if (!string.IsNullOrWhiteSpace(program.Icon)) if (!string.IsNullOrWhiteSpace(program.Icon))
{ {
artwork.Add(new Artwork artwork.Add(
{ new Artwork
ArtworkKind = ArtworkKind.Thumbnail, {
Path = program.Icon, ArtworkKind = ArtworkKind.Thumbnail,
SourcePath = program.Icon Path = program.Icon,
}); SourcePath = program.Icon
});
} }
return new Episode return new Episode
{ {
MediaVersions = MediaVersions =
@ -1100,7 +1103,7 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
{ {
EpisodeNumber = program.Episode, EpisodeNumber = program.Episode,
Title = program.Title Title = program.Title
}, }
], ],
Season = new Season Season = new Season
{ {
@ -1125,12 +1128,13 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
var artwork = new List<Artwork>(); var artwork = new List<Artwork>();
if (!string.IsNullOrWhiteSpace(program.Icon)) if (!string.IsNullOrWhiteSpace(program.Icon))
{ {
artwork.Add(new Artwork artwork.Add(
{ new Artwork
ArtworkKind = ArtworkKind.Poster, {
Path = program.Icon, ArtworkKind = ArtworkKind.Poster,
SourcePath = program.Icon Path = program.Icon,
}); SourcePath = program.Icon
});
} }
return new Movie return new Movie

10
ErsatzTV.Application/Channels/Commands/RefreshChannelListHandler.cs

@ -39,13 +39,13 @@ public class RefreshChannelListHandler : IRequestHandler<RefreshChannelList>
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "channel.sbntxt"); string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "channel.sbntxt");
// fall back to default template // fall back to default template
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_channel.sbntxt"); templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_channel.sbntxt");
} }
// fail if file doesn't exist // fail if file doesn't exist
if (!_localFileSystem.FileExists(templateFileName)) if (!_localFileSystem.FileExists(templateFileName))
{ {
@ -63,11 +63,11 @@ public class RefreshChannelListHandler : IRequestHandler<RefreshChannelList>
RemoveXmlComments = true, RemoveXmlComments = true,
CollapseTagsWithoutContent = true CollapseTagsWithoutContent = true
}); });
string text = await File.ReadAllTextAsync(templateFileName, cancellationToken); string text = await File.ReadAllTextAsync(templateFileName, cancellationToken);
var template = Template.Parse(text, templateFileName); var template = Template.Parse(text, templateFileName);
var templateContext = new XmlTemplateContext(); var templateContext = new XmlTemplateContext();
await using RecyclableMemoryStream ms = _recyclableMemoryStreamManager.GetStream(); await using RecyclableMemoryStream ms = _recyclableMemoryStreamManager.GetStream();
await using var xml = XmlWriter.Create( await using var xml = XmlWriter.Create(
ms, ms,
@ -83,7 +83,7 @@ public class RefreshChannelListHandler : IRequestHandler<RefreshChannelList>
ChannelHasArtwork = !string.IsNullOrWhiteSpace(channel.ArtworkPath), ChannelHasArtwork = !string.IsNullOrWhiteSpace(channel.ArtworkPath),
ChannelArtworkPath = channel.ArtworkPath ChannelArtworkPath = channel.ArtworkPath
}; };
var scriptObject = new ScriptObject(); var scriptObject = new ScriptObject();
scriptObject.Import(data); scriptObject.Import(data);
templateContext.PushGlobal(scriptObject); templateContext.PushGlobal(scriptObject);

3
ErsatzTV.Application/Channels/Commands/UpdateChannel.cs

@ -3,8 +3,7 @@ using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Channels; namespace ErsatzTV.Application.Channels;
public record UpdateChannel public record UpdateChannel(
(
int ChannelId, int ChannelId,
string Name, string Name,
string Number, string Number,

4
ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs

@ -71,7 +71,7 @@ public class UpdateChannelHandler(
c.WatermarkId = update.WatermarkId; c.WatermarkId = update.WatermarkId;
c.FallbackFillerId = update.FallbackFillerId; c.FallbackFillerId = update.FallbackFillerId;
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
searchTargets.SearchTargetsChanged(); searchTargets.SearchTargetsChanged();
if (c.SubtitleMode != ChannelSubtitleMode.None) if (c.SubtitleMode != ChannelSubtitleMode.None)
@ -84,7 +84,7 @@ public class UpdateChannelHandler(
await workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id)); await workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
} }
} }
await workerChannel.WriteAsync(new RefreshChannelList()); await workerChannel.WriteAsync(new RefreshChannelList());
return ProjectToViewModel(c); return ProjectToViewModel(c);

4
ErsatzTV.Application/Channels/Queries/GetChannelGuide.cs

@ -3,5 +3,5 @@ using ErsatzTV.Core.Iptv;
namespace ErsatzTV.Application.Channels; namespace ErsatzTV.Application.Channels;
public record GetChannelGuide public record GetChannelGuide(string Scheme, string Host, string BaseUrl, string AccessToken)
(string Scheme, string Host, string BaseUrl, string AccessToken) : IRequest<Either<BaseError, ChannelGuide>>; : IRequest<Either<BaseError, ChannelGuide>>;

18
ErsatzTV.Application/Configuration/Commands/UpdateGeneralSettingsHandler.cs

@ -26,16 +26,24 @@ public class UpdateGeneralSettingsHandler : IRequestHandler<UpdateGeneralSetting
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, generalSettings.DefaultMinimumLogLevel); await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, generalSettings.DefaultMinimumLogLevel);
_loggingLevelSwitches.DefaultLevelSwitch.MinimumLevel = generalSettings.DefaultMinimumLogLevel; _loggingLevelSwitches.DefaultLevelSwitch.MinimumLevel = generalSettings.DefaultMinimumLogLevel;
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevelScanning, generalSettings.ScanningMinimumLogLevel); await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelScanning,
generalSettings.ScanningMinimumLogLevel);
_loggingLevelSwitches.ScanningLevelSwitch.MinimumLevel = generalSettings.ScanningMinimumLogLevel; _loggingLevelSwitches.ScanningLevelSwitch.MinimumLevel = generalSettings.ScanningMinimumLogLevel;
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevelScheduling, generalSettings.SchedulingMinimumLogLevel); await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelScheduling,
generalSettings.SchedulingMinimumLogLevel);
_loggingLevelSwitches.SchedulingLevelSwitch.MinimumLevel = generalSettings.SchedulingMinimumLogLevel; _loggingLevelSwitches.SchedulingLevelSwitch.MinimumLevel = generalSettings.SchedulingMinimumLogLevel;
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevelStreaming, generalSettings.StreamingMinimumLogLevel); await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelStreaming,
generalSettings.StreamingMinimumLogLevel);
_loggingLevelSwitches.StreamingLevelSwitch.MinimumLevel = generalSettings.StreamingMinimumLogLevel; _loggingLevelSwitches.StreamingLevelSwitch.MinimumLevel = generalSettings.StreamingMinimumLogLevel;
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevelHttp, generalSettings.HttpMinimumLogLevel); await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelHttp,
generalSettings.HttpMinimumLogLevel);
_loggingLevelSwitches.HttpLevelSwitch.MinimumLevel = generalSettings.HttpMinimumLogLevel; _loggingLevelSwitches.HttpLevelSwitch.MinimumLevel = generalSettings.HttpMinimumLogLevel;
return Unit.Default; return Unit.Default;

2
ErsatzTV.Application/Configuration/Commands/UpdateXmltvSettingsHandler.cs

@ -31,4 +31,4 @@ public class UpdateXmltvSettingsHandler(
return Unit.Default; return Unit.Default;
} }
} }

6
ErsatzTV.Application/Configuration/Queries/GetGeneralSettingsHandler.cs

@ -15,16 +15,16 @@ public class GetGeneralSettingsHandler : IRequestHandler<GetGeneralSettings, Gen
{ {
Option<LogEventLevel> maybeDefaultLevel = Option<LogEventLevel> maybeDefaultLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel); await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel);
Option<LogEventLevel> maybeScanningLevel = Option<LogEventLevel> maybeScanningLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScanning); await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScanning);
Option<LogEventLevel> maybeSchedulingLevel = Option<LogEventLevel> maybeSchedulingLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScheduling); await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScheduling);
Option<LogEventLevel> maybeStreamingLevel = Option<LogEventLevel> maybeStreamingLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelStreaming); await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelStreaming);
Option<LogEventLevel> maybeHttpLevel = Option<LogEventLevel> maybeHttpLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelHttp); await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelHttp);

3
ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferences.cs

@ -2,7 +2,6 @@
namespace ErsatzTV.Application.Emby; namespace ErsatzTV.Application.Emby;
public record UpdateEmbyLibraryPreferences public record UpdateEmbyLibraryPreferences(List<EmbyLibraryPreference> Preferences) : IRequest<Either<BaseError, Unit>>;
(List<EmbyLibraryPreference> Preferences) : IRequest<Either<BaseError, Unit>>;
public record EmbyLibraryPreference(int Id, bool ShouldSyncItems); public record EmbyLibraryPreference(int Id, bool ShouldSyncItems);

2
ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferencesHandler.cs

@ -6,7 +6,7 @@ namespace ErsatzTV.Application.Emby;
public class public class
UpdateEmbyLibraryPreferencesHandler : IRequestHandler<UpdateEmbyLibraryPreferences, UpdateEmbyLibraryPreferencesHandler : IRequestHandler<UpdateEmbyLibraryPreferences,
Either<BaseError, Unit>> Either<BaseError, Unit>>
{ {
private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly ISearchIndex _searchIndex; private readonly ISearchIndex _searchIndex;

10
ErsatzTV.Application/Emby/EmbyLibraryViewModel.cs

@ -4,9 +4,9 @@ using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Emby; namespace ErsatzTV.Application.Emby;
public record EmbyLibraryViewModel( public record EmbyLibraryViewModel(
int Id, int Id,
string Name, string Name,
LibraryMediaKind MediaKind, LibraryMediaKind MediaKind,
bool ShouldSyncItems, bool ShouldSyncItems,
int MediaSourceId) int MediaSourceId)
: LibraryViewModel("Emby", Id, Name, MediaKind, MediaSourceId, string.Empty); : LibraryViewModel("Emby", Id, Name, MediaKind, MediaSourceId, string.Empty);

3
ErsatzTV.Application/Emby/Queries/GetEmbyPathReplacementsBySourceId.cs

@ -1,4 +1,3 @@
namespace ErsatzTV.Application.Emby; namespace ErsatzTV.Application.Emby;
public record GetEmbyPathReplacementsBySourceId public record GetEmbyPathReplacementsBySourceId(int EmbyMediaSourceId) : IRequest<List<EmbyPathReplacementViewModel>>;
(int EmbyMediaSourceId) : IRequest<List<EmbyPathReplacementViewModel>>;

4
ErsatzTV.Application/ErsatzTV.Application.csproj

@ -15,13 +15,13 @@
<PackageReference Include="MediatR" Version="12.2.0" /> <PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.8.14"> <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.9.28">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="3.0.0" /> <PackageReference Include="Serilog.Formatting.Compact.Reader" Version="3.0.0" />
<PackageReference Include="WebMarkupMin.Core" Version="2.14.0" /> <PackageReference Include="WebMarkupMin.Core" Version="2.16.0" />
<PackageReference Include="Winista.MimeDetect" Version="1.1.0" /> <PackageReference Include="Winista.MimeDetect" Version="1.1.0" />
</ItemGroup> </ItemGroup>

3
ErsatzTV.Application/FFmpegProfiles/Commands/CopyFFmpegProfile.cs

@ -2,5 +2,4 @@
namespace ErsatzTV.Application.FFmpegProfiles; namespace ErsatzTV.Application.FFmpegProfiles;
public record CopyFFmpegProfile public record CopyFFmpegProfile(int FFmpegProfileId, string Name) : IRequest<Either<BaseError, FFmpegProfileViewModel>>;
(int FFmpegProfileId, string Name) : IRequest<Either<BaseError, FFmpegProfileViewModel>>;

4
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs

@ -59,9 +59,9 @@ public class
p.NormalizeFramerate = update.NormalizeFramerate; p.NormalizeFramerate = update.NormalizeFramerate;
p.DeinterlaceVideo = update.DeinterlaceVideo; p.DeinterlaceVideo = update.DeinterlaceVideo;
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
_searchTargets.SearchTargetsChanged(); _searchTargets.SearchTargetsChanged();
return new UpdateFFmpegProfileResult(p.Id); return new UpdateFFmpegProfileResult(p.Id);
} }

4
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs

@ -101,11 +101,11 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler<UpdateFFmpegSettings,
{ {
request.Settings.ExtractEmbeddedSubtitles = false; request.Settings.ExtractEmbeddedSubtitles = false;
} }
await _configElementRepository.Upsert( await _configElementRepository.Upsert(
ConfigElementKey.FFmpegExtractEmbeddedSubtitles, ConfigElementKey.FFmpegExtractEmbeddedSubtitles,
request.Settings.ExtractEmbeddedSubtitles); request.Settings.ExtractEmbeddedSubtitles);
// queue extracting all embedded subtitles // queue extracting all embedded subtitles
if (request.Settings.ExtractEmbeddedSubtitles) if (request.Settings.ExtractEmbeddedSubtitles)
{ {

2
ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdForApiHandler.cs

@ -8,7 +8,7 @@ namespace ErsatzTV.Application.FFmpegProfiles;
public class public class
GetFFmpegProfileByIdForApiHandler : IRequestHandler<GetFFmpegFullProfileByIdForApi, GetFFmpegProfileByIdForApiHandler : IRequestHandler<GetFFmpegFullProfileByIdForApi,
Option<FFmpegFullProfileResponseModel>> Option<FFmpegFullProfileResponseModel>>
{ {
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;

2
ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs

@ -10,7 +10,7 @@ namespace ErsatzTV.Application.FFmpegProfiles;
public class public class
GetSupportedHardwareAccelerationKindsHandler : IRequestHandler<GetSupportedHardwareAccelerationKinds, GetSupportedHardwareAccelerationKindsHandler : IRequestHandler<GetSupportedHardwareAccelerationKinds,
List<HardwareAccelerationKind>> List<HardwareAccelerationKind>>
{ {
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IHardwareCapabilitiesFactory _hardwareCapabilitiesFactory; private readonly IHardwareCapabilitiesFactory _hardwareCapabilitiesFactory;

12
ErsatzTV.Application/Images/Commands/UpdateImageFolderDurationHandler.cs

@ -16,7 +16,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory<TvContext> dbCon
{ {
request = request with { ImageFolderDuration = 0.01 }; request = request with { ImageFolderDuration = 0.01 };
} }
// delete entry if null // delete entry if null
if (request.ImageFolderDuration is null) if (request.ImageFolderDuration is null)
{ {
@ -48,7 +48,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory<TvContext> dbCon
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
} }
} }
// update all images (bfs) starting at this folder // update all images (bfs) starting at this folder
Option<LibraryFolder> maybeFolder = await dbContext.LibraryFolders Option<LibraryFolder> maybeFolder = await dbContext.LibraryFolders
.AsNoTracking() .AsNoTracking()
@ -59,7 +59,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory<TvContext> dbCon
foreach (LibraryFolder libraryFolder in maybeFolder) foreach (LibraryFolder libraryFolder in maybeFolder)
{ {
LibraryFolder currentFolder = libraryFolder; LibraryFolder currentFolder = libraryFolder;
// walk up to get duration, if needed // walk up to get duration, if needed
double? durationSeconds = currentFolder.ImageFolderDuration?.DurationSeconds; double? durationSeconds = currentFolder.ImageFolderDuration?.DurationSeconds;
while (durationSeconds is null && currentFolder?.ParentId is not null) while (durationSeconds is null && currentFolder?.ParentId is not null)
@ -73,7 +73,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory<TvContext> dbCon
{ {
currentFolder = null; currentFolder = null;
} }
foreach (LibraryFolder parent in maybeParent) foreach (LibraryFolder parent in maybeParent)
{ {
currentFolder = parent; currentFolder = parent;
@ -83,7 +83,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory<TvContext> dbCon
queue.Enqueue(new FolderWithParentDuration(libraryFolder, durationSeconds)); queue.Enqueue(new FolderWithParentDuration(libraryFolder, durationSeconds));
} }
while (queue.Count > 0) while (queue.Count > 0)
{ {
(LibraryFolder currentFolder, double? parentDuration) = queue.Dequeue(); (LibraryFolder currentFolder, double? parentDuration) = queue.Dequeue();
@ -109,7 +109,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory<TvContext> dbCon
.Filter(lf => lf.ParentId == currentFolder.Id) .Filter(lf => lf.ParentId == currentFolder.Id)
.Include(lf => lf.ImageFolderDuration) .Include(lf => lf.ImageFolderDuration)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
// queue all children // queue all children
foreach (LibraryFolder child in children) foreach (LibraryFolder child in children)
{ {

5
ErsatzTV.Application/Images/Queries/GetCachedImagePath.cs

@ -3,6 +3,5 @@ using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Images; namespace ErsatzTV.Application.Images;
public record GetCachedImagePath public record GetCachedImagePath(string FileName, ArtworkKind ArtworkKind, int? MaxHeight = null) : IRequest<
(string FileName, ArtworkKind ArtworkKind, int? MaxHeight = null) : IRequest< Either<BaseError, CachedImagePathViewModel>>;
Either<BaseError, CachedImagePathViewModel>>;

2
ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinAdminUserIdHandler.cs

@ -10,7 +10,7 @@ namespace ErsatzTV.Application.Jellyfin;
public class public class
SynchronizeJellyfinAdminUserIdHandler : IRequestHandler<SynchronizeJellyfinAdminUserId, SynchronizeJellyfinAdminUserIdHandler : IRequestHandler<SynchronizeJellyfinAdminUserId,
Either<BaseError, Unit>> Either<BaseError, Unit>>
{ {
private readonly IJellyfinApiClient _jellyfinApiClient; private readonly IJellyfinApiClient _jellyfinApiClient;
private readonly IJellyfinSecretStore _jellyfinSecretStore; private readonly IJellyfinSecretStore _jellyfinSecretStore;

4
ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinLibraryById.cs

@ -16,8 +16,8 @@ public record SynchronizeJellyfinLibraryByIdIfNeeded(int JellyfinLibraryId) : IS
public bool DeepScan => false; public bool DeepScan => false;
} }
public record ForceSynchronizeJellyfinLibraryById public record ForceSynchronizeJellyfinLibraryById(int JellyfinLibraryId, bool DeepScan)
(int JellyfinLibraryId, bool DeepScan) : ISynchronizeJellyfinLibraryById : ISynchronizeJellyfinLibraryById
{ {
public bool ForceScan => true; public bool ForceScan => true;
} }

4
ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferences.cs

@ -2,7 +2,7 @@
namespace ErsatzTV.Application.Jellyfin; namespace ErsatzTV.Application.Jellyfin;
public record UpdateJellyfinLibraryPreferences public record UpdateJellyfinLibraryPreferences(List<JellyfinLibraryPreference> Preferences)
(List<JellyfinLibraryPreference> Preferences) : IRequest<Either<BaseError, Unit>>; : IRequest<Either<BaseError, Unit>>;
public record JellyfinLibraryPreference(int Id, bool ShouldSyncItems); public record JellyfinLibraryPreference(int Id, bool ShouldSyncItems);

2
ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferencesHandler.cs

@ -6,7 +6,7 @@ namespace ErsatzTV.Application.Jellyfin;
public class public class
UpdateJellyfinLibraryPreferencesHandler : IRequestHandler<UpdateJellyfinLibraryPreferences, UpdateJellyfinLibraryPreferencesHandler : IRequestHandler<UpdateJellyfinLibraryPreferences,
Either<BaseError, Unit>> Either<BaseError, Unit>>
{ {
private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly ISearchIndex _searchIndex; private readonly ISearchIndex _searchIndex;

10
ErsatzTV.Application/Jellyfin/JellyfinLibraryViewModel.cs

@ -4,9 +4,9 @@ using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Jellyfin; namespace ErsatzTV.Application.Jellyfin;
public record JellyfinLibraryViewModel( public record JellyfinLibraryViewModel(
int Id, int Id,
string Name, string Name,
LibraryMediaKind MediaKind, LibraryMediaKind MediaKind,
bool ShouldSyncItems, bool ShouldSyncItems,
int MediaSourceId) int MediaSourceId)
: LibraryViewModel("Jellyfin", Id, Name, MediaKind, MediaSourceId, string.Empty); : LibraryViewModel("Jellyfin", Id, Name, MediaKind, MediaSourceId, string.Empty);

2
ErsatzTV.Application/Jellyfin/Queries/GetAllJellyfinMediaSourcesHandler.cs

@ -5,7 +5,7 @@ namespace ErsatzTV.Application.Jellyfin;
public class public class
GetAllJellyfinMediaSourcesHandler : IRequestHandler<GetAllJellyfinMediaSources, GetAllJellyfinMediaSourcesHandler : IRequestHandler<GetAllJellyfinMediaSources,
List<JellyfinMediaSourceViewModel>> List<JellyfinMediaSourceViewModel>>
{ {
private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IMediaSourceRepository _mediaSourceRepository;

2
ErsatzTV.Application/Jellyfin/Queries/GetJellyfinLibrariesBySourceIdHandler.cs

@ -5,7 +5,7 @@ namespace ErsatzTV.Application.Jellyfin;
public class public class
GetJellyfinLibrariesBySourceIdHandler : IRequestHandler<GetJellyfinLibrariesBySourceId, GetJellyfinLibrariesBySourceIdHandler : IRequestHandler<GetJellyfinLibrariesBySourceId,
List<JellyfinLibraryViewModel>> List<JellyfinLibraryViewModel>>
{ {
private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IMediaSourceRepository _mediaSourceRepository;

3
ErsatzTV.Application/Jellyfin/Queries/GetJellyfinMediaSourceById.cs

@ -1,4 +1,3 @@
namespace ErsatzTV.Application.Jellyfin; namespace ErsatzTV.Application.Jellyfin;
public record GetJellyfinMediaSourceById public record GetJellyfinMediaSourceById(int JellyfinMediaSourceId) : IRequest<Option<JellyfinMediaSourceViewModel>>;
(int JellyfinMediaSourceId) : IRequest<Option<JellyfinMediaSourceViewModel>>;

2
ErsatzTV.Application/Jellyfin/Queries/GetJellyfinMediaSourceByIdHandler.cs

@ -5,7 +5,7 @@ namespace ErsatzTV.Application.Jellyfin;
public class public class
GetJellyfinMediaSourceByIdHandler : IRequestHandler<GetJellyfinMediaSourceById, GetJellyfinMediaSourceByIdHandler : IRequestHandler<GetJellyfinMediaSourceById,
Option<JellyfinMediaSourceViewModel>> Option<JellyfinMediaSourceViewModel>>
{ {
private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IMediaSourceRepository _mediaSourceRepository;

4
ErsatzTV.Application/Jellyfin/Queries/GetJellyfinPathReplacementsBySourceId.cs

@ -1,4 +1,4 @@
namespace ErsatzTV.Application.Jellyfin; namespace ErsatzTV.Application.Jellyfin;
public record GetJellyfinPathReplacementsBySourceId public record GetJellyfinPathReplacementsBySourceId(int JellyfinMediaSourceId)
(int JellyfinMediaSourceId) : IRequest<List<JellyfinPathReplacementViewModel>>; : IRequest<List<JellyfinPathReplacementViewModel>>;

11
ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs

@ -13,6 +13,7 @@ using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog; using Serilog;
using Serilog.Core;
using Serilog.Events; using Serilog.Events;
using Serilog.Formatting.Compact.Reader; using Serilog.Formatting.Compact.Reader;
@ -20,6 +21,7 @@ namespace ErsatzTV.Application.Libraries;
public abstract class CallLibraryScannerHandler<TRequest> public abstract class CallLibraryScannerHandler<TRequest>
{ {
private readonly int _batchSize = 100;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _channel; private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _channel;
private readonly IConfigElementRepository _configElementRepository; private readonly IConfigElementRepository _configElementRepository;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
@ -27,7 +29,6 @@ public abstract class CallLibraryScannerHandler<TRequest>
private readonly IRuntimeInfo _runtimeInfo; private readonly IRuntimeInfo _runtimeInfo;
private readonly List<int> _toReindex = []; private readonly List<int> _toReindex = [];
private readonly List<int> _toRemove = []; private readonly List<int> _toRemove = [];
private readonly int _batchSize = 100;
private string _libraryName; private string _libraryName;
protected CallLibraryScannerHandler( protected CallLibraryScannerHandler(
@ -74,7 +75,7 @@ public abstract class CallLibraryScannerHandler<TRequest>
await _channel.WriteAsync(new ReindexMediaItems(_toReindex.ToArray()), cancellationToken); await _channel.WriteAsync(new ReindexMediaItems(_toReindex.ToArray()), cancellationToken);
_toReindex.Clear(); _toReindex.Clear();
} }
if (_toRemove.Count > 0) if (_toRemove.Count > 0)
{ {
await _channel.WriteAsync(new RemoveMediaItems(_toReindex.ToArray()), cancellationToken); await _channel.WriteAsync(new RemoveMediaItems(_toReindex.ToArray()), cancellationToken);
@ -104,10 +105,10 @@ public abstract class CallLibraryScannerHandler<TRequest>
if (logEvent.Properties.TryGetValue("SourceContext", out LogEventPropertyValue property)) if (logEvent.Properties.TryGetValue("SourceContext", out LogEventPropertyValue property))
{ {
log = log.ForContext( log = log.ForContext(
Serilog.Core.Constants.SourceContextPropertyName, Constants.SourceContextPropertyName,
property.ToString().Trim('"')); property.ToString().Trim('"'));
} }
log.Write( log.Write(
new LogEvent( new LogEvent(
logEvent.Timestamp.ToLocalTime(), logEvent.Timestamp.ToLocalTime(),
@ -143,7 +144,7 @@ public abstract class CallLibraryScannerHandler<TRequest>
await _channel.WriteAsync(new ReindexMediaItems(_toReindex.ToArray())); await _channel.WriteAsync(new ReindexMediaItems(_toReindex.ToArray()));
_toReindex.Clear(); _toReindex.Clear();
} }
_toRemove.AddRange(progressUpdate.ItemsToRemove); _toRemove.AddRange(progressUpdate.ItemsToRemove);
if (_toRemove.Count >= _batchSize) if (_toRemove.Count >= _batchSize)
{ {

2
ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryHandler.cs

@ -32,7 +32,7 @@ public class CreateLocalLibraryHandler : LocalLibraryHandlerBase,
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, LocalLibrary> validation = await Validate(dbContext, request); Validation<BaseError, LocalLibrary> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, localLibrary => PersistLocalLibrary(dbContext, localLibrary)); return await validation.Apply(localLibrary => PersistLocalLibrary(dbContext, localLibrary));
} }
private async Task<LocalLibraryViewModel> PersistLocalLibrary( private async Task<LocalLibraryViewModel> PersistLocalLibrary(

4
ErsatzTV.Application/Libraries/Commands/CreateLocalLibraryPath.cs

@ -2,5 +2,5 @@
namespace ErsatzTV.Application.Libraries; namespace ErsatzTV.Application.Libraries;
public record CreateLocalLibraryPath public record CreateLocalLibraryPath(int LibraryId, string Path)
(int LibraryId, string Path) : IRequest<Either<BaseError, LocalLibraryPathViewModel>>; : IRequest<Either<BaseError, LocalLibraryPathViewModel>>;

2
ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryHandler.cs

@ -28,7 +28,7 @@ public class DeleteLocalLibraryHandler : LocalLibraryHandlerBase,
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, LocalLibrary> validation = await LocalLibraryMustExist(dbContext, request); Validation<BaseError, LocalLibrary> validation = await LocalLibraryMustExist(dbContext, request);
return await LanguageExtensions.Apply(validation, localLibrary => DoDeletion(dbContext, localLibrary)); return await validation.Apply(localLibrary => DoDeletion(dbContext, localLibrary));
} }
private async Task<Unit> DoDeletion(TvContext dbContext, LocalLibrary localLibrary) private async Task<Unit> DoDeletion(TvContext dbContext, LocalLibrary localLibrary)

2
ErsatzTV.Application/Libraries/Commands/MoveLocalLibraryPathHandler.cs

@ -39,7 +39,7 @@ public class MoveLocalLibraryPathHandler : IRequestHandler<MoveLocalLibraryPath,
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, parameters => MovePath(dbContext, parameters)); return await validation.Apply(parameters => MovePath(dbContext, parameters));
} }
private async Task<Unit> MovePath(TvContext dbContext, Parameters parameters) private async Task<Unit> MovePath(TvContext dbContext, Parameters parameters)

2
ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs

@ -37,7 +37,7 @@ public class UpdateLocalLibraryHandler : LocalLibraryHandlerBase,
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, parameters => UpdateLocalLibrary(dbContext, parameters)); return await validation.Apply(parameters => UpdateLocalLibrary(dbContext, parameters));
} }
private async Task<LocalLibraryViewModel> UpdateLocalLibrary(TvContext dbContext, Parameters parameters) private async Task<LocalLibraryViewModel> UpdateLocalLibrary(TvContext dbContext, Parameters parameters)

10
ErsatzTV.Application/Libraries/PlexLibraryViewModel.cs

@ -3,9 +3,9 @@
namespace ErsatzTV.Application.Libraries; namespace ErsatzTV.Application.Libraries;
public record PlexLibraryViewModel( public record PlexLibraryViewModel(
int Id, int Id,
string Name, string Name,
LibraryMediaKind MediaKind, LibraryMediaKind MediaKind,
int MediaSourceId, int MediaSourceId,
string MediaSourceName) string MediaSourceName)
: LibraryViewModel("Plex", Id, Name, MediaKind, MediaSourceId, MediaSourceName); : LibraryViewModel("Plex", Id, Name, MediaKind, MediaSourceId, MediaSourceName);

3
ErsatzTV.Application/MediaCards/ArtistCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record ArtistCardViewModel public record ArtistCardViewModel(
(
int ArtistId, int ArtistId,
string Title, string Title,
string Subtitle, string Subtitle,

3
ErsatzTV.Application/MediaCards/ImageCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record ImageCardViewModel public record ImageCardViewModel(
(
int ImageId, int ImageId,
string Title, string Title,
string Subtitle, string Subtitle,

3
ErsatzTV.Application/MediaCards/MovieCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record MovieCardViewModel public record MovieCardViewModel(
(
int MovieId, int MovieId,
string Title, string Title,
string Subtitle, string Subtitle,

3
ErsatzTV.Application/MediaCards/MusicVideoCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record MusicVideoCardViewModel public record MusicVideoCardViewModel(
(
int MusicVideoId, int MusicVideoId,
string Title, string Title,
string Subtitle, string Subtitle,

3
ErsatzTV.Application/MediaCards/OtherVideoCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record OtherVideoCardViewModel public record OtherVideoCardViewModel(
(
int OtherVideoId, int OtherVideoId,
string Title, string Title,
string Subtitle, string Subtitle,

3
ErsatzTV.Application/MediaCards/Queries/GetMusicVideoCards.cs

@ -1,4 +1,3 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record GetMusicVideoCards public record GetMusicVideoCards(int ArtistId, int PageNumber, int PageSize) : IRequest<MusicVideoCardResultsViewModel>;
(int ArtistId, int PageNumber, int PageSize) : IRequest<MusicVideoCardResultsViewModel>;

4
ErsatzTV.Application/MediaCards/Queries/GetTelevisionEpisodeCards.cs

@ -1,4 +1,4 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record GetTelevisionEpisodeCards public record GetTelevisionEpisodeCards(int TelevisionSeasonId, int PageNumber, int PageSize)
(int TelevisionSeasonId, int PageNumber, int PageSize) : IRequest<TelevisionEpisodeCardResultsViewModel>; : IRequest<TelevisionEpisodeCardResultsViewModel>;

4
ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCards.cs

@ -1,4 +1,4 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record GetTelevisionSeasonCards public record GetTelevisionSeasonCards(int TelevisionShowId, int PageNumber, int PageSize)
(int TelevisionShowId, int PageNumber, int PageSize) : IRequest<TelevisionSeasonCardResultsViewModel>; : IRequest<TelevisionSeasonCardResultsViewModel>;

2
ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCardsHandler.cs

@ -6,7 +6,7 @@ namespace ErsatzTV.Application.MediaCards;
public class public class
GetTelevisionSeasonCardsHandler : IRequestHandler<GetTelevisionSeasonCards, GetTelevisionSeasonCardsHandler : IRequestHandler<GetTelevisionSeasonCards,
TelevisionSeasonCardResultsViewModel> TelevisionSeasonCardResultsViewModel>
{ {
private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly ITelevisionRepository _televisionRepository; private readonly ITelevisionRepository _televisionRepository;

3
ErsatzTV.Application/MediaCards/SongCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record SongCardViewModel public record SongCardViewModel(
(
int SongId, int SongId,
string Title, string Title,
string Subtitle, string Subtitle,

3
ErsatzTV.Application/MediaCards/TelevisionEpisodeCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record TelevisionEpisodeCardViewModel public record TelevisionEpisodeCardViewModel(
(
int EpisodeId, int EpisodeId,
DateTime Aired, DateTime Aired,
string ShowTitle, string ShowTitle,

3
ErsatzTV.Application/MediaCards/TelevisionSeasonCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record TelevisionSeasonCardViewModel public record TelevisionSeasonCardViewModel(
(
string ShowTitle, string ShowTitle,
int TelevisionSeasonId, int TelevisionSeasonId,
int TelevisionSeasonNumber, int TelevisionSeasonNumber,

3
ErsatzTV.Application/MediaCards/TelevisionShowCardViewModel.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCards; namespace ErsatzTV.Application.MediaCards;
public record TelevisionShowCardViewModel public record TelevisionShowCardViewModel(
(
int TelevisionShowId, int TelevisionShowId,
string Title, string Title,
string Subtitle, string Subtitle,

3
ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollection.cs

@ -2,5 +2,4 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record AddArtistToCollection public record AddArtistToCollection(int CollectionId, int ArtistId) : IRequest<Either<BaseError, Unit>>;
(int CollectionId, int ArtistId) : IRequest<Either<BaseError, Unit>>;

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

@ -33,7 +33,7 @@ public class AddArtistToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, parameters => ApplyAddArtistRequest(dbContext, parameters)); return await validation.Apply(parameters => ApplyAddArtistRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddArtistRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddArtistRequest(TvContext dbContext, Parameters parameters)

4
ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs

@ -33,9 +33,7 @@ public class AddEpisodeToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply( return await validation.Apply(parameters => ApplyAddTelevisionEpisodeRequest(dbContext, parameters));
validation,
parameters => ApplyAddTelevisionEpisodeRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddTelevisionEpisodeRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddTelevisionEpisodeRequest(TvContext dbContext, Parameters parameters)

3
ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollection.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record AddItemsToCollection public record AddItemsToCollection(
(
int CollectionId, int CollectionId,
List<int> MovieIds, List<int> MovieIds,
List<int> ShowIds, List<int> ShowIds,

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

@ -33,7 +33,7 @@ public class AddMovieToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, parameters => ApplyAddMovieRequest(dbContext, parameters)); return await validation.Apply(parameters => ApplyAddMovieRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddMovieRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddMovieRequest(TvContext dbContext, Parameters parameters)

3
ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollection.cs

@ -2,5 +2,4 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record AddMusicVideoToCollection public record AddMusicVideoToCollection(int CollectionId, int MusicVideoId) : IRequest<Either<BaseError, Unit>>;
(int CollectionId, int MusicVideoId) : IRequest<Either<BaseError, Unit>>;

4
ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs

@ -33,9 +33,7 @@ public class AddMusicVideoToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply( return await validation.Apply(parameters => ApplyAddMusicVideoRequest(dbContext, parameters));
validation,
parameters => ApplyAddMusicVideoRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddMusicVideoRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddMusicVideoRequest(TvContext dbContext, Parameters parameters)

3
ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollection.cs

@ -2,5 +2,4 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record AddOtherVideoToCollection public record AddOtherVideoToCollection(int CollectionId, int OtherVideoId) : IRequest<Either<BaseError, Unit>>;
(int CollectionId, int OtherVideoId) : IRequest<Either<BaseError, Unit>>;

4
ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs

@ -33,9 +33,7 @@ public class AddOtherVideoToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply( return await validation.Apply(parameters => ApplyAddOtherVideoRequest(dbContext, parameters));
validation,
parameters => ApplyAddOtherVideoRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddOtherVideoRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddOtherVideoRequest(TvContext dbContext, Parameters parameters)

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

@ -33,7 +33,7 @@ public class AddSeasonToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, parameters => ApplyAddSeasonRequest(dbContext, parameters)); return await validation.Apply(parameters => ApplyAddSeasonRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddSeasonRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddSeasonRequest(TvContext dbContext, Parameters parameters)

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

@ -33,7 +33,7 @@ public class AddShowToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, parameters => ApplyAddShowRequest(dbContext, parameters)); return await validation.Apply(parameters => ApplyAddShowRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddShowRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddShowRequest(TvContext dbContext, Parameters parameters)

3
ErsatzTV.Application/MediaCollections/Commands/AddSongToCollection.cs

@ -2,5 +2,4 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record AddSongToCollection public record AddSongToCollection(int CollectionId, int SongId) : IRequest<Either<BaseError, Unit>>;
(int CollectionId, int SongId) : IRequest<Either<BaseError, Unit>>;

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

@ -33,7 +33,7 @@ public class AddSongToCollectionHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Parameters> validation = await Validate(dbContext, request); Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, parameters => ApplyAddSongRequest(dbContext, parameters)); return await validation.Apply(parameters => ApplyAddSongRequest(dbContext, parameters));
} }
private async Task<Unit> ApplyAddSongRequest(TvContext dbContext, Parameters parameters) private async Task<Unit> ApplyAddSongRequest(TvContext dbContext, Parameters parameters)

4
ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollection.cs

@ -9,5 +9,5 @@ public record CreateMultiCollectionItem(
bool ScheduleAsGroup, bool ScheduleAsGroup,
PlaybackOrder PlaybackOrder); PlaybackOrder PlaybackOrder);
public record CreateMultiCollection public record CreateMultiCollection(string Name, List<CreateMultiCollectionItem> Items)
(string Name, List<CreateMultiCollectionItem> Items) : IRequest<Either<BaseError, MultiCollectionViewModel>>; : IRequest<Either<BaseError, MultiCollectionViewModel>>;

3
ErsatzTV.Application/MediaCollections/Commands/CreateSmartCollection.cs

@ -2,5 +2,4 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record CreateSmartCollection public record CreateSmartCollection(string Query, string Name) : IRequest<Either<BaseError, SmartCollectionViewModel>>;
(string Query, string Name) : IRequest<Either<BaseError, SmartCollectionViewModel>>;

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

@ -44,7 +44,7 @@ public class DeleteTraktListHandler : TraktCommandBase, IRequestHandler<DeleteTr
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, TraktList> validation = await TraktListMustExist(dbContext, request.TraktListId); Validation<BaseError, TraktList> validation = await TraktListMustExist(dbContext, request.TraktListId);
return await LanguageExtensions.Apply(validation, c => DoDeletion(dbContext, c)); return await validation.Apply(c => DoDeletion(dbContext, c));
} }
finally finally
{ {

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

@ -32,7 +32,7 @@ public class RemoveItemsFromCollectionHandler : IRequestHandler<RemoveItemsFromC
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Collection> validation = await Validate(dbContext, request); Validation<BaseError, Collection> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, c => ApplyRemoveItemsRequest(dbContext, request, c)); return await validation.Apply(c => ApplyRemoveItemsRequest(dbContext, request, c));
} }
private async Task<Unit> ApplyRemoveItemsRequest( private async Task<Unit> ApplyRemoveItemsRequest(

3
ErsatzTV.Application/MediaCollections/Commands/UpdateCollection.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record UpdateCollection public record UpdateCollection(int CollectionId, string Name) : IRequest<Either<BaseError, Unit>>
(int CollectionId, string Name) : IRequest<Either<BaseError, Unit>>
{ {
public Option<bool> UseCustomPlaybackOrder { get; set; } = None; public Option<bool> UseCustomPlaybackOrder { get; set; } = None;
} }

3
ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionCustomOrder.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.MediaCollections; namespace ErsatzTV.Application.MediaCollections;
public record UpdateCollectionCustomOrder public record UpdateCollectionCustomOrder(
(
int CollectionId, int CollectionId,
List<MediaItemCustomOrder> MediaItemCustomOrders) : IRequest<Either<BaseError, Unit>>; List<MediaItemCustomOrder> MediaItemCustomOrders) : IRequest<Either<BaseError, Unit>>;

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

@ -32,7 +32,7 @@ public class UpdateCollectionCustomOrderHandler : IRequestHandler<UpdateCollecti
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Collection> validation = await Validate(dbContext, request); Validation<BaseError, Collection> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, c => ApplyUpdateRequest(dbContext, c, request)); return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request));
} }
private async Task<Unit> ApplyUpdateRequest( private async Task<Unit> ApplyUpdateRequest(

4
ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs

@ -14,9 +14,9 @@ namespace ErsatzTV.Application.MediaCollections;
public class UpdateCollectionHandler : IRequestHandler<UpdateCollection, Either<BaseError, Unit>> public class UpdateCollectionHandler : IRequestHandler<UpdateCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ISearchTargets _searchTargets;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
private readonly ISearchTargets _searchTargets;
public UpdateCollectionHandler( public UpdateCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
@ -56,7 +56,7 @@ public class UpdateCollectionHandler : IRequestHandler<UpdateCollection, Either<
await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh)); await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh));
} }
} }
_searchTargets.SearchTargetsChanged(); _searchTargets.SearchTargetsChanged();
return Unit.Default; return Unit.Default;

3
ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollection.cs

@ -9,8 +9,7 @@ public record UpdateMultiCollectionItem(
bool ScheduleAsGroup, bool ScheduleAsGroup,
PlaybackOrder PlaybackOrder); PlaybackOrder PlaybackOrder);
public record UpdateMultiCollection public record UpdateMultiCollection(
(
int MultiCollectionId, int MultiCollectionId,
string Name, string Name,
List<UpdateMultiCollectionItem> Items) : IRequest<Either<BaseError, Unit>>; List<UpdateMultiCollectionItem> Items) : IRequest<Either<BaseError, Unit>>;

6
ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs

@ -14,9 +14,9 @@ namespace ErsatzTV.Application.MediaCollections;
public class UpdateMultiCollectionHandler : IRequestHandler<UpdateMultiCollection, Either<BaseError, Unit>> public class UpdateMultiCollectionHandler : IRequestHandler<UpdateMultiCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ISearchTargets _searchTargets;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
private readonly ISearchTargets _searchTargets;
public UpdateMultiCollectionHandler( public UpdateMultiCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
@ -36,7 +36,7 @@ public class UpdateMultiCollectionHandler : IRequestHandler<UpdateMultiCollectio
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, MultiCollection> validation = await Validate(dbContext, request); Validation<BaseError, MultiCollection> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, c => ApplyUpdateRequest(dbContext, c, request)); return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request));
} }
private async Task<Unit> ApplyUpdateRequest(TvContext dbContext, MultiCollection c, UpdateMultiCollection request) private async Task<Unit> ApplyUpdateRequest(TvContext dbContext, MultiCollection c, UpdateMultiCollection request)
@ -120,7 +120,7 @@ public class UpdateMultiCollectionHandler : IRequestHandler<UpdateMultiCollectio
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
_searchTargets.SearchTargetsChanged(); _searchTargets.SearchTargetsChanged();
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository.PlayoutIdsUsingMultiCollection( foreach (int playoutId in await _mediaCollectionRepository.PlayoutIdsUsingMultiCollection(
request.MultiCollectionId)) request.MultiCollectionId))

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

@ -14,9 +14,9 @@ namespace ErsatzTV.Application.MediaCollections;
public class UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollection, Either<BaseError, Unit>> public class UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ISearchTargets _searchTargets;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
private readonly ISearchTargets _searchTargets;
public UpdateSmartCollectionHandler( public UpdateSmartCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,

7
ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs

@ -17,13 +17,13 @@ namespace ErsatzTV.Application.Playouts;
public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseError, Unit>> public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseError, Unit>>
{ {
private readonly IBlockPlayoutBuilder _blockPlayoutBuilder;
private readonly IClient _client; private readonly IClient _client;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IEntityLocker _entityLocker; private readonly IEntityLocker _entityLocker;
private readonly IExternalJsonPlayoutBuilder _externalJsonPlayoutBuilder;
private readonly IFFmpegSegmenterService _ffmpegSegmenterService; private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
private readonly IPlayoutBuilder _playoutBuilder; private readonly IPlayoutBuilder _playoutBuilder;
private readonly IBlockPlayoutBuilder _blockPlayoutBuilder;
private readonly IExternalJsonPlayoutBuilder _externalJsonPlayoutBuilder;
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel; private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
public BuildPlayoutHandler( public BuildPlayoutHandler(
@ -100,7 +100,8 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
foreach (string channelNumber in maybeChannelNumber) foreach (string channelNumber in maybeChannelNumber)
{ {
string fileName = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channelNumber}.xml"); string fileName = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channelNumber}.xml");
if (hasChanges || !File.Exists(fileName) || playout.ProgramSchedulePlayoutType is ProgramSchedulePlayoutType.ExternalJson) if (hasChanges || !File.Exists(fileName) ||
playout.ProgramSchedulePlayoutType is ProgramSchedulePlayoutType.ExternalJson)
{ {
await _workerChannel.WriteAsync(new RefreshChannelData(channelNumber), cancellationToken); await _workerChannel.WriteAsync(new RefreshChannelData(channelNumber), cancellationToken);
} }

4
ErsatzTV.Application/Playouts/Commands/CreateExternalJsonPlayoutHandler.cs

@ -14,9 +14,9 @@ namespace ErsatzTV.Application.Playouts;
public class CreateExternalJsonPlayoutHandler public class CreateExternalJsonPlayoutHandler
: IRequestHandler<CreateExternalJsonPlayout, Either<BaseError, CreatePlayoutResponse>> : IRequestHandler<CreateExternalJsonPlayout, Either<BaseError, CreatePlayoutResponse>>
{ {
private readonly ILocalFileSystem _localFileSystem;
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ILocalFileSystem _localFileSystem;
public CreateExternalJsonPlayoutHandler( public CreateExternalJsonPlayoutHandler(
ILocalFileSystem localFileSystem, ILocalFileSystem localFileSystem,
@ -27,7 +27,7 @@ public class CreateExternalJsonPlayoutHandler
_channel = channel; _channel = channel;
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
} }
public async Task<Either<BaseError, CreatePlayoutResponse>> Handle( public async Task<Either<BaseError, CreatePlayoutResponse>> Handle(
CreateExternalJsonPlayout request, CreateExternalJsonPlayout request,
CancellationToken cancellationToken) CancellationToken cancellationToken)

4
ErsatzTV.Application/Playouts/Commands/CreateFloodPlayoutHandler.cs

@ -41,7 +41,9 @@ public class CreateFloodPlayoutHandler : IRequestHandler<CreateFloodPlayout, Eit
return new CreatePlayoutResponse(playout.Id); return new CreatePlayoutResponse(playout.Id);
} }
private static async Task<Validation<BaseError, Playout>> Validate(TvContext dbContext, CreateFloodPlayout request) => private static async Task<Validation<BaseError, Playout>> Validate(
TvContext dbContext,
CreateFloodPlayout request) =>
(await ValidateChannel(dbContext, request), await ValidateProgramSchedule(dbContext, request), (await ValidateChannel(dbContext, request), await ValidateProgramSchedule(dbContext, request),
ValidatePlayoutType(request)) ValidatePlayoutType(request))
.Apply( .Apply(

4
ErsatzTV.Application/Playouts/Commands/ReplacePlayoutAlternateScheduleItems.cs

@ -2,5 +2,5 @@ using ErsatzTV.Core;
namespace ErsatzTV.Application.Playouts; namespace ErsatzTV.Application.Playouts;
public record ReplacePlayoutAlternateScheduleItems public record ReplacePlayoutAlternateScheduleItems(int PlayoutId, List<ReplacePlayoutAlternateSchedule> Items)
(int PlayoutId, List<ReplacePlayoutAlternateSchedule> Items) : IRequest<Either<BaseError, Unit>>; : IRequest<Either<BaseError, Unit>>;

8
ErsatzTV.Application/Playouts/Commands/UpdateExternalJsonPlayoutHandler.cs

@ -8,7 +8,9 @@ using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Playouts; namespace ErsatzTV.Application.Playouts;
public class UpdateExternalJsonPlayoutHandler : IRequestHandler<UpdateExternalJsonPlayout, Either<BaseError, PlayoutNameViewModel>> public class
UpdateExternalJsonPlayoutHandler : IRequestHandler<UpdateExternalJsonPlayout,
Either<BaseError, PlayoutNameViewModel>>
{ {
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel; private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
@ -52,7 +54,9 @@ public class UpdateExternalJsonPlayoutHandler : IRequestHandler<UpdateExternalJs
Optional(playout.DailyRebuildTime)); Optional(playout.DailyRebuildTime));
} }
private static Task<Validation<BaseError, Playout>> Validate(TvContext dbContext, UpdateExternalJsonPlayout request) => private static Task<Validation<BaseError, Playout>> Validate(
TvContext dbContext,
UpdateExternalJsonPlayout request) =>
PlayoutMustExist(dbContext, request); PlayoutMustExist(dbContext, request);
private static Task<Validation<BaseError, Playout>> PlayoutMustExist( private static Task<Validation<BaseError, Playout>> PlayoutMustExist(

4
ErsatzTV.Application/Playouts/Commands/UpdatePlayout.cs

@ -2,5 +2,5 @@
namespace ErsatzTV.Application.Playouts; namespace ErsatzTV.Application.Playouts;
public record UpdatePlayout public record UpdatePlayout(int PlayoutId, Option<TimeSpan> DailyRebuildTime)
(int PlayoutId, Option<TimeSpan> DailyRebuildTime) : IRequest<Either<BaseError, PlayoutNameViewModel>>; : IRequest<Either<BaseError, PlayoutNameViewModel>>;

4
ErsatzTV.Application/Playouts/Queries/GetFuturePlayoutItemsById.cs

@ -1,4 +1,4 @@
namespace ErsatzTV.Application.Playouts; namespace ErsatzTV.Application.Playouts;
public record GetFuturePlayoutItemsById public record GetFuturePlayoutItemsById(int PlayoutId, bool ShowFiller, int PageNum, int PageSize)
(int PlayoutId, bool ShowFiller, int PageNum, int PageSize) : IRequest<PagedPlayoutItemsViewModel>; : IRequest<PagedPlayoutItemsViewModel>;

3
ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferences.cs

@ -2,7 +2,6 @@
namespace ErsatzTV.Application.Plex; namespace ErsatzTV.Application.Plex;
public record UpdatePlexLibraryPreferences public record UpdatePlexLibraryPreferences(List<PlexLibraryPreference> Preferences) : IRequest<Either<BaseError, Unit>>;
(List<PlexLibraryPreference> Preferences) : IRequest<Either<BaseError, Unit>>;
public record PlexLibraryPreference(int Id, bool ShouldSyncItems); public record PlexLibraryPreference(int Id, bool ShouldSyncItems);

2
ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs

@ -6,7 +6,7 @@ namespace ErsatzTV.Application.Plex;
public class public class
UpdatePlexLibraryPreferencesHandler : IRequestHandler<UpdatePlexLibraryPreferences, UpdatePlexLibraryPreferencesHandler : IRequestHandler<UpdatePlexLibraryPreferences,
Either<BaseError, Unit>> Either<BaseError, Unit>>
{ {
private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly ISearchIndex _searchIndex; private readonly ISearchIndex _searchIndex;

3
ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacements.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.Plex; namespace ErsatzTV.Application.Plex;
public record UpdatePlexPathReplacements public record UpdatePlexPathReplacements(
(
int PlexMediaSourceId, int PlexMediaSourceId,
List<PlexPathReplacementItem> PathReplacements) : IRequest<Either<BaseError, Unit>>; List<PlexPathReplacementItem> PathReplacements) : IRequest<Either<BaseError, Unit>>;

4
ErsatzTV.Application/Plex/Queries/GetPlexConnectionParameters.cs

@ -2,5 +2,5 @@
namespace ErsatzTV.Application.Plex; namespace ErsatzTV.Application.Plex;
public record GetPlexConnectionParameters public record GetPlexConnectionParameters(int PlexMediaSourceId)
(int PlexMediaSourceId) : IRequest<Either<BaseError, PlexConnectionParametersViewModel>>; : IRequest<Either<BaseError, PlexConnectionParametersViewModel>>;

3
ErsatzTV.Application/Plex/Queries/GetPlexPathReplacementsBySourceId.cs

@ -1,4 +1,3 @@
namespace ErsatzTV.Application.Plex; namespace ErsatzTV.Application.Plex;
public record GetPlexPathReplacementsBySourceId public record GetPlexPathReplacementsBySourceId(int PlexMediaSourceId) : IRequest<List<PlexPathReplacementViewModel>>;
(int PlexMediaSourceId) : IRequest<List<PlexPathReplacementViewModel>>;

2
ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItemHandler.cs

@ -29,7 +29,7 @@ public class AddProgramScheduleItemHandler : ProgramScheduleItemCommandBase,
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, ProgramSchedule> validation = await Validate(dbContext, request); Validation<BaseError, ProgramSchedule> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, ps => PersistItem(dbContext, request, ps)); return await validation.Apply(ps => PersistItem(dbContext, request, ps));
} }
private async Task<ProgramScheduleItemViewModel> PersistItem( private async Task<ProgramScheduleItemViewModel> PersistItem(

4
ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramSchedule.cs

@ -2,5 +2,5 @@
namespace ErsatzTV.Application.ProgramSchedules; namespace ErsatzTV.Application.ProgramSchedules;
public record CopyProgramSchedule public record CopyProgramSchedule(int ProgramScheduleId, string Name)
(int ProgramScheduleId, string Name) : IRequest<Either<BaseError, ProgramScheduleViewModel>>; : IRequest<Either<BaseError, ProgramScheduleViewModel>>;

5
ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs

@ -32,6 +32,5 @@ public record ReplaceProgramScheduleItem(
string PreferredSubtitleLanguageCode, string PreferredSubtitleLanguageCode,
ChannelSubtitleMode? SubtitleMode) : IProgramScheduleItemRequest; ChannelSubtitleMode? SubtitleMode) : IProgramScheduleItemRequest;
public record ReplaceProgramScheduleItems public record ReplaceProgramScheduleItems(int ProgramScheduleId, List<ReplaceProgramScheduleItem> Items) : IRequest<
(int ProgramScheduleId, List<ReplaceProgramScheduleItem> Items) : IRequest< Either<BaseError, IEnumerable<ProgramScheduleItemViewModel>>>;
Either<BaseError, IEnumerable<ProgramScheduleItemViewModel>>>;

3
ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramSchedule.cs

@ -2,8 +2,7 @@
namespace ErsatzTV.Application.ProgramSchedules; namespace ErsatzTV.Application.ProgramSchedules;
public record UpdateProgramSchedule public record UpdateProgramSchedule(
(
int ProgramScheduleId, int ProgramScheduleId,
string Name, string Name,
bool KeepMultiPartEpisodesTogether, bool KeepMultiPartEpisodesTogether,

2
ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramScheduleHandler.cs

@ -29,7 +29,7 @@ public class UpdateProgramScheduleHandler :
{ {
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, ProgramSchedule> validation = await Validate(dbContext, request); Validation<BaseError, ProgramSchedule> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, ps => ApplyUpdateRequest(dbContext, ps, request)); return await validation.Apply(ps => ApplyUpdateRequest(dbContext, ps, request));
} }
private async Task<UpdateProgramScheduleResult> ApplyUpdateRequest( private async Task<UpdateProgramScheduleResult> ApplyUpdateRequest(

8
ErsatzTV.Application/Scheduling/Commands/CreateBlockGroupHandler.cs

@ -8,13 +8,15 @@ namespace ErsatzTV.Application.Scheduling;
public class CreateBlockGroupHandler(IDbContextFactory<TvContext> dbContextFactory) public class CreateBlockGroupHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<CreateBlockGroup, Either<BaseError, BlockGroupViewModel>> : IRequestHandler<CreateBlockGroup, Either<BaseError, BlockGroupViewModel>>
{ {
public async Task<Either<BaseError, BlockGroupViewModel>> Handle(CreateBlockGroup request, CancellationToken cancellationToken) public async Task<Either<BaseError, BlockGroupViewModel>> Handle(
CreateBlockGroup request,
CancellationToken cancellationToken)
{ {
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, BlockGroup> validation = await Validate(request); Validation<BaseError, BlockGroup> validation = await Validate(request);
return await validation.Apply(profile => PersistBlockGroup(dbContext, profile)); return await validation.Apply(profile => PersistBlockGroup(dbContext, profile));
} }
private static async Task<BlockGroupViewModel> PersistBlockGroup(TvContext dbContext, BlockGroup blockGroup) private static async Task<BlockGroupViewModel> PersistBlockGroup(TvContext dbContext, BlockGroup blockGroup)
{ {
await dbContext.BlockGroups.AddAsync(blockGroup); await dbContext.BlockGroups.AddAsync(blockGroup);
@ -24,7 +26,7 @@ public class CreateBlockGroupHandler(IDbContextFactory<TvContext> dbContextFacto
private static Task<Validation<BaseError, BlockGroup>> Validate(CreateBlockGroup request) => private static Task<Validation<BaseError, BlockGroup>> Validate(CreateBlockGroup request) =>
Task.FromResult(ValidateName(request).Map(name => new BlockGroup { Name = name, Blocks = [] })); Task.FromResult(ValidateName(request).Map(name => new BlockGroup { Name = name, Blocks = [] }));
private static Validation<BaseError, string> ValidateName(CreateBlockGroup createBlockGroup) => private static Validation<BaseError, string> ValidateName(CreateBlockGroup createBlockGroup) =>
createBlockGroup.NotEmpty(x => x.Name) createBlockGroup.NotEmpty(x => x.Name)
.Bind(_ => createBlockGroup.NotLongerThan(50)(x => x.Name)); .Bind(_ => createBlockGroup.NotLongerThan(50)(x => x.Name));

39
ErsatzTV.Application/Scheduling/Commands/CreateBlockHandler.cs

@ -13,7 +13,7 @@ public class CreateBlockHandler(IDbContextFactory<TvContext> dbContextFactory)
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Block> validation = await Validate(request); Validation<BaseError, Block> validation = await Validate(dbContext, request);
return await validation.Apply(profile => PersistBlock(dbContext, profile)); return await validation.Apply(profile => PersistBlock(dbContext, profile));
} }
@ -24,17 +24,30 @@ public class CreateBlockHandler(IDbContextFactory<TvContext> dbContextFactory)
return Mapper.ProjectToViewModel(block); return Mapper.ProjectToViewModel(block);
} }
private static Task<Validation<BaseError, Block>> Validate(CreateBlock request) => private static async Task<Validation<BaseError, Block>> Validate(TvContext dbContext, CreateBlock request) =>
Task.FromResult( await ValidateBlockName(dbContext, request).MapT(
ValidateName(request).Map( name => new Block
name => new Block {
{ BlockGroupId = request.BlockGroupId,
BlockGroupId = request.BlockGroupId, Name = name,
Name = name, Minutes = 30
Minutes = 30 });
}));
private static Validation<BaseError, string> ValidateName(CreateBlock createBlock) => private static async Task<Validation<BaseError, string>> ValidateBlockName(
createBlock.NotEmpty(x => x.Name) TvContext dbContext,
.Bind(_ => createBlock.NotLongerThan(50)(x => x.Name)); CreateBlock request)
{
if (request.Name.Length > 50)
{
return BaseError.New($"Block name \"{request.Name}\" is invalid");
}
Option<Block> maybeExisting = await dbContext.Blocks
.FirstOrDefaultAsync(r => r.BlockGroupId == request.BlockGroupId && r.Name == request.Name)
.Map(Optional);
return maybeExisting.IsSome
? BaseError.New($"A block named \"{request.Name}\" already exists in that block group")
: request.Name;
}
} }

2
ErsatzTV.Application/Scheduling/Commands/EraseBlockPlayoutItemsHandler.cs

@ -40,7 +40,7 @@ public class EraseBlockPlayoutItemsHandler(IDbContextFactory<TvContext> dbContex
// save history changes // save history changes
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
// delete all playout items // delete all playout items
await dbContext.Database.ExecuteSqlAsync( await dbContext.Database.ExecuteSqlAsync(
$"DELETE FROM PlayoutItem WHERE PlayoutId = {playout.Id}", $"DELETE FROM PlayoutItem WHERE PlayoutId = {playout.Id}",

6
ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItemsHandler.cs

@ -28,7 +28,7 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact
block.Minutes = request.Minutes; block.Minutes = request.Minutes;
block.StopScheduling = request.StopScheduling; block.StopScheduling = request.StopScheduling;
block.DateUpdated = DateTime.UtcNow; block.DateUpdated = DateTime.UtcNow;
dbContext.RemoveRange(block.Items); dbContext.RemoveRange(block.Items);
block.Items = request.Items.Map(i => BuildItem(block, i.Index, i)).ToList(); block.Items = request.Items.Map(i => BuildItem(block, i.Index, i)).ToList();
@ -67,7 +67,7 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact
.Include(b => b.Items) .Include(b => b.Items)
.SelectOneAsync(b => b.Id, b => b.Id == blockId) .SelectOneAsync(b => b.Id, b => b.Id == blockId)
.Map(o => o.ToValidation<BaseError>("[BlockId] does not exist.")); .Map(o => o.ToValidation<BaseError>("[BlockId] does not exist."));
private static Validation<BaseError, Block> MinutesMustBeValid(ReplaceBlockItems request, Block block) => private static Validation<BaseError, Block> MinutesMustBeValid(ReplaceBlockItems request, Block block) =>
Optional(block) Optional(block)
.Filter(_ => request.Minutes > 0 && request.Minutes % 15 == 0 && request.Minutes <= 24 * 60) .Filter(_ => request.Minutes > 0 && request.Minutes % 15 == 0 && request.Minutes <= 24 * 60)
@ -75,7 +75,7 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact
private static Validation<BaseError, Block> CollectionTypesMustBeValid(ReplaceBlockItems request, Block block) => private static Validation<BaseError, Block> CollectionTypesMustBeValid(ReplaceBlockItems request, Block block) =>
request.Items.Map(item => CollectionTypeMustBeValid(item, block)).Sequence().Map(_ => block); request.Items.Map(item => CollectionTypeMustBeValid(item, block)).Sequence().Map(_ => block);
private static Validation<BaseError, Block> CollectionTypeMustBeValid(ReplaceBlockItem item, Block block) private static Validation<BaseError, Block> CollectionTypeMustBeValid(ReplaceBlockItem item, Block block)
{ {
switch (item.CollectionType) switch (item.CollectionType)

4
ErsatzTV.Application/Scheduling/Commands/ReplacePlayoutTemplateItemsHandler.cs

@ -42,7 +42,7 @@ public class ReplacePlayoutTemplateItemsHandler(
playout.Templates.Remove(remove); playout.Templates.Remove(remove);
} }
var now = DateTime.UtcNow; DateTime now = DateTime.UtcNow;
foreach (ReplacePlayoutTemplate add in toAdd) foreach (ReplacePlayoutTemplate add in toAdd)
{ {
@ -71,7 +71,7 @@ public class ReplacePlayoutTemplateItemsHandler(
ex.DateUpdated = now; ex.DateUpdated = now;
} }
} }
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
} }

3
ErsatzTV.Application/Scheduling/Queries/GetTemplateItemsHandler.cs

@ -3,7 +3,8 @@ using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Scheduling; namespace ErsatzTV.Application.Scheduling;
public class GetTemplateItemsHandler(IDbContextFactory<TvContext> dbContextFactory) : IRequestHandler<GetTemplateItems, List<TemplateItemViewModel>> public class GetTemplateItemsHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetTemplateItems, List<TemplateItemViewModel>>
{ {
public async Task<List<TemplateItemViewModel>> Handle(GetTemplateItems request, CancellationToken cancellationToken) public async Task<List<TemplateItemViewModel>> Handle(GetTemplateItems request, CancellationToken cancellationToken)
{ {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save