Browse Source

trakt list fixes (#2560)

* show reset playout build failures

* fix scheduling trakt list playlists that contain shows
pull/2561/head
Jason Dove 9 months ago committed by GitHub
parent
commit
e089b12c2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 4
      ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayout.cs
  3. 8
      ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs
  4. 3
      ErsatzTV.Application/MediaCollections/Commands/TraktCommandBase.cs
  5. 3
      ErsatzTV.Core/Errors/SchedulingLoopEncountered.cs
  6. 21
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  7. 20
      ErsatzTV/Pages/PlaylistEditor.razor
  8. 23
      ErsatzTV/Services/RunOnce/DatabaseCleanerService.cs

2
CHANGELOG.md

@ -45,6 +45,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -45,6 +45,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix NVIDIA overlaying text subtitles and permanent watermark on 10-bit content
- Fix UI error adding deco
- Fix UI error editing watermarks and graphics elements on blocks
- Fix showing playout build failure details when resetting a playout
- Fix scheduling auto-generated trakt list playlists that contain shows
### Changed
- Do not use graphics engine for single, permanent watermark

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

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
using ErsatzTV.Application.Scheduling;
using ErsatzTV.Core;
namespace ErsatzTV.Application.MediaCollections;
public record PreviewPlaylistPlayout(ReplacePlaylistItems Data) : IRequest<List<PlayoutItemPreviewViewModel>>;
public record PreviewPlaylistPlayout(ReplacePlaylistItems Data)
: IRequest<Either<BaseError, List<PlayoutItemPreviewViewModel>>>;

8
ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs

@ -17,9 +17,9 @@ public class PreviewPlaylistPlayoutHandler( @@ -17,9 +17,9 @@ public class PreviewPlaylistPlayoutHandler(
IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository,
IPlayoutBuilder playoutBuilder)
: IRequestHandler<PreviewPlaylistPlayout, List<PlayoutItemPreviewViewModel>>
: IRequestHandler<PreviewPlaylistPlayout, Either<BaseError, List<PlayoutItemPreviewViewModel>>>
{
public async Task<List<PlayoutItemPreviewViewModel>> Handle(
public async Task<Either<BaseError, List<PlayoutItemPreviewViewModel>>> Handle(
PreviewPlaylistPlayout request,
CancellationToken cancellationToken)
{
@ -65,7 +65,7 @@ public class PreviewPlaylistPlayoutHandler( @@ -65,7 +65,7 @@ public class PreviewPlaylistPlayoutHandler(
PlayoutBuildMode.Reset,
cancellationToken);
return await buildResult.MatchAsync(
return await buildResult.MatchAsync<Either<BaseError, List<PlayoutItemPreviewViewModel>>>(
async result =>
{
var maxItems = 0;
@ -121,7 +121,7 @@ public class PreviewPlaylistPlayoutHandler( @@ -121,7 +121,7 @@ public class PreviewPlaylistPlayoutHandler(
return onceThrough.OrderBy(i => i.StartOffset).Map(Scheduling.Mapper.ProjectToViewModel).ToList();
},
_ => []);
error => error);
}
private static ProgramScheduleItemFlood MapToScheduleItem(PreviewPlaylistPlayout request) =>

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

@ -195,7 +195,8 @@ public abstract class TraktCommandBase @@ -195,7 +195,8 @@ public abstract class TraktCommandBase
Index = item.Rank,
PlaylistId = list.Playlist.Id,
Playlist = list.Playlist,
IncludeInProgramGuide = true
IncludeInProgramGuide = true,
PlaybackOrder = PlaybackOrder.Chronological
};
await dbContext.PlaylistItems.AddAsync(playlistItem, cancellationToken);

3
ErsatzTV.Core/Errors/SchedulingLoopEncountered.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Core.Errors;
public class SchedulingLoopEncountered() : BaseError("Scheduling loop encountered");

21
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System.Reflection;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
@ -296,7 +297,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -296,7 +297,7 @@ public class PlayoutBuilder : IPlayoutBuilder
TrimStart = false;
}
await BuildPlayoutItems(
Either<BaseError, PlayoutBuildResult> maybeResult = await BuildPlayoutItems(
playout,
referenceData,
result,
@ -306,6 +307,16 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -306,6 +307,16 @@ public class PlayoutBuilder : IPlayoutBuilder
true,
cancellationToken);
foreach (BaseError error in maybeResult.LeftToSeq())
{
return error;
}
foreach (PlayoutBuildResult r in maybeResult.RightToSeq())
{
result = r;
}
// time shift on demand channel if needed
if (referenceData.Channel.PlayoutMode is ChannelPlayoutMode.OnDemand)
{
@ -350,6 +361,12 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -350,6 +361,12 @@ public class PlayoutBuilder : IPlayoutBuilder
PlayoutReferenceData referenceData,
CancellationToken cancellationToken)
{
if (referenceData.ProgramSchedule.Items.Count == 0)
{
_logger.LogWarning("Playout {Playout}'s schedule has no schedule items", referenceData.Channel.Name);
return BaseError.New($"Playout {referenceData.Channel.Name}'s schedule has no schedule items");
}
Map<CollectionKey, List<MediaItem>> collectionMediaItems =
await GetCollectionMediaItems(referenceData, cancellationToken);
if (collectionMediaItems.IsEmpty)
@ -802,7 +819,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -802,7 +819,7 @@ public class PlayoutBuilder : IPlayoutBuilder
"Failed to schedule beyond {Time}; aborting playout build - this can be caused by an impossible schedule, or by a bug",
playoutBuilderState.CurrentTime);
throw new InvalidOperationException("Scheduling loop encountered");
return new SchedulingLoopEncountered();
}
// _logger.LogDebug("Playout time is {CurrentTime}", playoutBuilderState.CurrentTime);

20
ErsatzTV/Pages/PlaylistEditor.razor

@ -312,6 +312,12 @@ @@ -312,6 +312,12 @@
<MudTextField @bind-Value="@_selectedItem.Count" For="@(() => _selectedItem.Count)"/>
</MudStack>
}
else if (!string.IsNullOrWhiteSpace(_previewMessage))
{
<MudText Typo="Typo.h5" Class="mt-10 mb-2">Preview</MudText>
<MudDivider Class="mb-6"/>
<MudAlert Severity="Severity.Warning">@_previewMessage</MudAlert>
}
else if (_previewItems is not null)
{
<MudText Typo="Typo.h5" Class="mt-10 mb-2">Preview</MudText>
@ -348,6 +354,7 @@ else if (_previewItems is not null) @@ -348,6 +354,7 @@ else if (_previewItems is not null)
private PlaylistItemsEditViewModel _playlist = new() { Items = [] };
private PlaylistItemEditViewModel _selectedItem;
private string _previewMessage;
private List<PlayoutItemPreviewViewModel> _previewItems;
public void Dispose()
@ -577,7 +584,18 @@ else if (_previewItems is not null) @@ -577,7 +584,18 @@ else if (_previewItems is not null)
private async Task PreviewPlayout()
{
_selectedItem = null;
_previewItems = await Mediator.Send(new PreviewPlaylistPlayout(GenerateReplaceRequest()), _cts.Token);
_previewMessage = null;
Either<BaseError, List<PlayoutItemPreviewViewModel>> result = await Mediator.Send(new PreviewPlaylistPlayout(GenerateReplaceRequest()), _cts.Token);
foreach (var error in result.LeftToSeq())
{
_previewMessage = error.ToString();
}
foreach (List<PlayoutItemPreviewViewModel> items in result.RightToSeq())
{
_previewItems = items;
}
}
private static void UpdateEPG(PlaylistItemEditViewModel context, bool includeInProgramGuide) => context.IncludeInProgramGuide = includeInProgramGuide;

23
ErsatzTV/Services/RunOnce/DatabaseCleanerService.cs

@ -32,6 +32,7 @@ public class DatabaseCleanerService( @@ -32,6 +32,7 @@ public class DatabaseCleanerService(
await DeleteInvalidMediaItems(dbContext);
await GenerateFallbackMetadata(scope, dbContext, stoppingToken);
await ResetExternalSubtitleState(dbContext, stoppingToken);
await RemoveOrphanedPlaylists(dbContext, stoppingToken);
systemStartup.DatabaseIsCleaned();
@ -90,4 +91,26 @@ public class DatabaseCleanerService( @@ -90,4 +91,26 @@ public class DatabaseCleanerService(
"UPDATE `Subtitle` SET `IsExtracted`=0 WHERE `Path` IS NULL",
cancellationToken);
}
private static async Task RemoveOrphanedPlaylists(TvContext dbContext, CancellationToken cancellationToken)
{
// remove orphaned playlists
await dbContext.Playlists
.Where(p => p.IsSystem && !dbContext.TraktLists.Any(t => t.PlaylistId == p.Id))
.ExecuteDeleteAsync(cancellationToken);
// turn off "generate playlist" on trakt lists with no referenced playlist
await dbContext.TraktLists
.Where(t => t.Playlist == null && t.GeneratePlaylist)
.ExecuteUpdateAsync(
setters => setters.SetProperty(t => t.GeneratePlaylist, false),
cancellationToken);
// set playback order when unset
await dbContext.PlaylistItems
.Where(pi => pi.Playlist.IsSystem && pi.PlaybackOrder == PlaybackOrder.None)
.ExecuteUpdateAsync(
setters => setters.SetProperty(p => p.PlaybackOrder, PlaybackOrder.Chronological),
cancellationToken);
}
}

Loading…
Cancel
Save