Browse Source

add graphics elements on classic schedule items (#2380)

pull/2369/head
Jason Dove 9 months ago committed by GitHub
parent
commit
40ab7c2cff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 7
      ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs
  3. 2
      ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs
  4. 9
      ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramScheduleHandler.cs
  5. 1
      ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs
  6. 11
      ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs
  7. 1
      ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs
  8. 8
      ErsatzTV.Application/ProgramSchedules/Mapper.cs
  9. 3
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs
  10. 3
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs
  11. 3
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs
  12. 3
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs
  13. 2
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs
  14. 2
      ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleItemsHandler.cs
  15. 7
      ErsatzTV.Core.Tests/Scheduling/ScheduleIntegrationTests.cs
  16. 2
      ErsatzTV.Core/Domain/GraphicsElement.cs
  17. 2
      ErsatzTV.Core/Domain/ProgramScheduleItem.cs
  18. 9
      ErsatzTV.Core/Domain/ProgramScheduleItemGraphicsElement.cs
  19. 14
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs
  20. 14
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs
  21. 14
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs
  22. 14
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs
  23. 6366
      ErsatzTV.Infrastructure.MySql/Migrations/20250905114217_Add_ProgramScheduleItemGraphicsElements.Designer.cs
  24. 51
      ErsatzTV.Infrastructure.MySql/Migrations/20250905114217_Add_ProgramScheduleItemGraphicsElements.cs
  25. 38
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  26. 6201
      ErsatzTV.Infrastructure.Sqlite/Migrations/20250905114029_Add_ProgramScheduleItemGraphicsElements.Designer.cs
  27. 50
      ErsatzTV.Infrastructure.Sqlite/Migrations/20250905114029_Add_ProgramScheduleItemGraphicsElements.cs
  28. 38
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  29. 13
      ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemConfiguration.cs
  30. 24
      ErsatzTV/Pages/ScheduleItemsEditor.razor
  31. 2
      ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs

3
CHANGELOG.md

@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- Classic schedules: allow selecting multiple graphics elements on schedule items
### Fixed
- Fix transcoding content with bt709/pc color metadata
- Fix scripted schedule validation (file exists) when creating or editing playout

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

@ -375,6 +375,9 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -375,6 +375,9 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
.ThenInclude(psi => psi.ProgramScheduleItemWatermarks)
.ThenInclude(psi => psi.Watermark)
.Include(ps => ps.Items)
.ThenInclude(psi => psi.ProgramScheduleItemGraphicsElements)
.ThenInclude(psi => psi.GraphicsElement)
.Include(ps => ps.Items)
.ThenInclude(psi => psi.Collection)
.Include(ps => ps.Items)
.ThenInclude(psi => psi.MediaItem)
@ -399,6 +402,10 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro @@ -399,6 +402,10 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
.ThenInclude(psi => psi.Watermark)
.Include(a => a.ProgramSchedule)
.ThenInclude(ps => ps.Items)
.ThenInclude(psi => psi.ProgramScheduleItemGraphicsElements)
.ThenInclude(psi => psi.GraphicsElement)
.Include(a => a.ProgramSchedule)
.ThenInclude(ps => ps.Items)
.ThenInclude(psi => psi.Collection)
.Include(a => a.ProgramSchedule)
.ThenInclude(ps => ps.Items)

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

@ -30,8 +30,8 @@ public record AddProgramScheduleItem( @@ -30,8 +30,8 @@ public record AddProgramScheduleItem(
int? PostRollFillerId,
int? TailFillerId,
int? FallbackFillerId,
int? WatermarkId,
List<int> WatermarkIds,
List<int> GraphicsElementIds,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
string PreferredSubtitleLanguageCode,

9
ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramScheduleHandler.cs

@ -56,6 +56,13 @@ public class @@ -56,6 +56,13 @@ public class
watermark.ProgramScheduleItemId = 0;
watermark.ProgramScheduleItem = item;
}
foreach (ProgramScheduleItemGraphicsElement graphicsElement in item.ProgramScheduleItemGraphicsElements)
{
DetachEntity(dbContext, graphicsElement);
graphicsElement.ProgramScheduleItemId = 0;
graphicsElement.ProgramScheduleItem = item;
}
}
await dbContext.ProgramSchedules.AddAsync(schedule, cancellationToken);
@ -81,6 +88,8 @@ public class @@ -81,6 +88,8 @@ public class
.AsNoTracking()
.Include(ps => ps.Items)
.ThenInclude(ps => ps.ProgramScheduleItemWatermarks)
.Include(ps => ps.Items)
.ThenInclude(ps => ps.ProgramScheduleItemGraphicsElements)
.SelectOneAsync(p => p.Id, p => p.Id == request.ProgramScheduleId, cancellationToken)
.Map(o => o.ToValidation<BaseError>("Schedule does not exist."));

1
ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs

@ -29,6 +29,7 @@ public interface IProgramScheduleItemRequest @@ -29,6 +29,7 @@ public interface IProgramScheduleItemRequest
int? TailFillerId { get; }
int? FallbackFillerId { get; }
List<int> WatermarkIds { get; }
List<int> GraphicsElementIds { get; }
string PreferredAudioLanguageCode { get; }
string PreferredAudioTitle { get; }
string PreferredSubtitleLanguageCode { get; }

11
ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs

@ -314,6 +314,17 @@ public abstract class ProgramScheduleItemCommandBase @@ -314,6 +314,17 @@ public abstract class ProgramScheduleItemCommandBase
});
}
foreach (int graphicsElementId in item.GraphicsElementIds)
{
result.ProgramScheduleItemGraphicsElements ??= [];
result.ProgramScheduleItemGraphicsElements.Add(
new ProgramScheduleItemGraphicsElement
{
ProgramScheduleItem = result,
GraphicsElementId = graphicsElementId
});
}
return result;
}

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

@ -31,6 +31,7 @@ public record ReplaceProgramScheduleItem( @@ -31,6 +31,7 @@ public record ReplaceProgramScheduleItem(
int? TailFillerId,
int? FallbackFillerId,
List<int> WatermarkIds,
List<int> GraphicsElementIds,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
string PreferredSubtitleLanguageCode,

8
ErsatzTV.Application/ProgramSchedules/Mapper.cs

@ -68,6 +68,8 @@ internal static class Mapper @@ -68,6 +68,8 @@ internal static class Mapper
: null,
duration.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
.ToList(),
duration.ProgramScheduleItemGraphicsElements.Map(ge => Graphics.Mapper.ProjectToViewModel(ge.GraphicsElement))
.ToList(),
duration.PreferredAudioLanguageCode,
duration.PreferredAudioTitle,
duration.PreferredSubtitleLanguageCode,
@ -120,6 +122,8 @@ internal static class Mapper @@ -120,6 +122,8 @@ internal static class Mapper
: null,
flood.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
.ToList(),
flood.ProgramScheduleItemGraphicsElements.Map(ge => Graphics.Mapper.ProjectToViewModel(ge.GraphicsElement))
.ToList(),
flood.PreferredAudioLanguageCode,
flood.PreferredAudioTitle,
flood.PreferredSubtitleLanguageCode,
@ -174,6 +178,8 @@ internal static class Mapper @@ -174,6 +178,8 @@ internal static class Mapper
: null,
multiple.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
.ToList(),
multiple.ProgramScheduleItemGraphicsElements.Map(ge => Graphics.Mapper.ProjectToViewModel(ge.GraphicsElement))
.ToList(),
multiple.PreferredAudioLanguageCode,
multiple.PreferredAudioTitle,
multiple.PreferredSubtitleLanguageCode,
@ -226,6 +232,8 @@ internal static class Mapper @@ -226,6 +232,8 @@ internal static class Mapper
: null,
one.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark))
.ToList(),
one.ProgramScheduleItemGraphicsElements.Map(ge => Graphics.Mapper.ProjectToViewModel(ge.GraphicsElement))
.ToList(),
one.PreferredAudioLanguageCode,
one.PreferredAudioTitle,
one.PreferredSubtitleLanguageCode,

3
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Application.Filler;
using ErsatzTV.Application.Graphics;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Application.Watermarks;
@ -34,6 +35,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode @@ -34,6 +35,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode
FillerPresetViewModel tailFiller,
FillerPresetViewModel fallbackFiller,
List<WatermarkViewModel> watermarks,
List<GraphicsElementViewModel> graphicsElements,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
@ -60,6 +62,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode @@ -60,6 +62,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode
tailFiller,
fallbackFiller,
watermarks,
graphicsElements,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,

3
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Application.Filler;
using ErsatzTV.Application.Graphics;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Application.Watermarks;
@ -31,6 +32,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel @@ -31,6 +32,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel
FillerPresetViewModel tailFiller,
FillerPresetViewModel fallbackFiller,
List<WatermarkViewModel> watermarks,
List<GraphicsElementViewModel> graphicsElements,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
@ -57,6 +59,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel @@ -57,6 +59,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel
tailFiller,
fallbackFiller,
watermarks,
graphicsElements,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,

3
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Application.Filler;
using ErsatzTV.Application.Graphics;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Application.Watermarks;
@ -33,6 +34,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode @@ -33,6 +34,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode
FillerPresetViewModel tailFiller,
FillerPresetViewModel fallbackFiller,
List<WatermarkViewModel> watermarks,
List<GraphicsElementViewModel> graphicsElements,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
@ -59,6 +61,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode @@ -59,6 +61,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode
tailFiller,
fallbackFiller,
watermarks,
graphicsElements,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,

3
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Application.Filler;
using ErsatzTV.Application.Graphics;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Application.Watermarks;
@ -31,6 +32,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel @@ -31,6 +32,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel
FillerPresetViewModel tailFiller,
FillerPresetViewModel fallbackFiller,
List<WatermarkViewModel> watermarks,
List<GraphicsElementViewModel> graphicsElements,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
@ -57,6 +59,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel @@ -57,6 +59,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel
tailFiller,
fallbackFiller,
watermarks,
graphicsElements,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,

2
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Application.Filler;
using ErsatzTV.Application.Graphics;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Application.Watermarks;
@ -30,6 +31,7 @@ public abstract record ProgramScheduleItemViewModel( @@ -30,6 +31,7 @@ public abstract record ProgramScheduleItemViewModel(
FillerPresetViewModel TailFiller,
FillerPresetViewModel FallbackFiller,
List<WatermarkViewModel> Watermarks,
List<GraphicsElementViewModel> GraphicsElements,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
string PreferredSubtitleLanguageCode,

2
ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleItemsHandler.cs

@ -47,6 +47,8 @@ public class GetProgramScheduleItemsHandler(IDbContextFactory<TvContext> dbConte @@ -47,6 +47,8 @@ public class GetProgramScheduleItemsHandler(IDbContextFactory<TvContext> dbConte
.Include(i => i.FallbackFiller)
.Include(i => i.ProgramScheduleItemWatermarks)
.ThenInclude(i => i.Watermark)
.Include(i => i.ProgramScheduleItemGraphicsElements)
.ThenInclude(i => i.GraphicsElement)
.ToListAsync(cancellationToken)
.Map(programScheduleItems => programScheduleItems.Map(ProjectToViewModel)
.Map(psi => EnforceProperties(maybeProgramSchedule, psi)).ToList());

7
ErsatzTV.Core.Tests/Scheduling/ScheduleIntegrationTests.cs

@ -439,6 +439,9 @@ public class ScheduleIntegrationTests @@ -439,6 +439,9 @@ public class ScheduleIntegrationTests
.ThenInclude(psi => psi.ProgramScheduleItemWatermarks)
.ThenInclude(psi => psi.Watermark)
.Include(ps => ps.Items)
.ThenInclude(psi => psi.ProgramScheduleItemGraphicsElements)
.ThenInclude(psi => psi.GraphicsElement)
.Include(ps => ps.Items)
.ThenInclude(psi => psi.Collection)
.Include(ps => ps.Items)
.ThenInclude(psi => psi.MediaItem)
@ -463,6 +466,10 @@ public class ScheduleIntegrationTests @@ -463,6 +466,10 @@ public class ScheduleIntegrationTests
.ThenInclude(psi => psi.Watermark)
.Include(a => a.ProgramSchedule)
.ThenInclude(ps => ps.Items)
.ThenInclude(psi => psi.ProgramScheduleItemGraphicsElements)
.ThenInclude(psi => psi.GraphicsElement)
.Include(a => a.ProgramSchedule)
.ThenInclude(ps => ps.Items)
.ThenInclude(psi => psi.Collection)
.Include(a => a.ProgramSchedule)
.ThenInclude(ps => ps.Items)

2
ErsatzTV.Core/Domain/GraphicsElement.cs

@ -7,4 +7,6 @@ public class GraphicsElement @@ -7,4 +7,6 @@ public class GraphicsElement
public GraphicsElementKind Kind { get; set; }
public List<PlayoutItem> PlayoutItems { get; set; }
public List<PlayoutItemGraphicsElement> PlayoutItemGraphicsElements { get; set; }
public List<ProgramScheduleItem> ProgramScheduleItems { get; set; }
public List<ProgramScheduleItemGraphicsElement> ProgramScheduleItemGraphicsElements { get; set; }
}

2
ErsatzTV.Core/Domain/ProgramScheduleItem.cs

@ -43,6 +43,8 @@ public abstract class ProgramScheduleItem @@ -43,6 +43,8 @@ public abstract class ProgramScheduleItem
public FillerPreset FallbackFiller { get; set; }
public List<ChannelWatermark> Watermarks { get; set; }
public List<ProgramScheduleItemWatermark> ProgramScheduleItemWatermarks { get; set; }
public List<GraphicsElement> GraphicsElements { get; set; }
public List<ProgramScheduleItemGraphicsElement> ProgramScheduleItemGraphicsElements { get; set; }
public string PreferredAudioLanguageCode { get; set; }
public string PreferredAudioTitle { get; set; }
public string PreferredSubtitleLanguageCode { get; set; }

9
ErsatzTV.Core/Domain/ProgramScheduleItemGraphicsElement.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
namespace ErsatzTV.Core.Domain;
public class ProgramScheduleItemGraphicsElement
{
public int ProgramScheduleItemId { get; set; }
public ProgramScheduleItem ProgramScheduleItem { get; set; }
public int GraphicsElementId { get; set; }
public GraphicsElement GraphicsElement { get; set; }
}

14
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs

@ -162,7 +162,8 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche @@ -162,7 +162,8 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode,
PlayoutItemWatermarks = []
PlayoutItemWatermarks = [],
PlayoutItemGraphicsElements = []
};
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
@ -176,6 +177,17 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche @@ -176,6 +177,17 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche
});
}
foreach (ProgramScheduleItemGraphicsElement programScheduleItemGraphicsElement in scheduleItem
.ProgramScheduleItemGraphicsElements ?? [])
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = programScheduleItemGraphicsElement.GraphicsElementId
});
}
durationUntil.Do(du => playoutItem.GuideFinish = du.UtcDateTime);
DateTimeOffset durationFinish = nextState.DurationFinish.IfNone(SystemTime.MaxValueUtc);

14
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs

@ -81,7 +81,8 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul @@ -81,7 +81,8 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode,
PlayoutItemWatermarks = []
PlayoutItemWatermarks = [],
PlayoutItemGraphicsElements = []
};
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
@ -95,6 +96,17 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul @@ -95,6 +96,17 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
});
}
foreach (ProgramScheduleItemGraphicsElement programScheduleItemGraphicsElement in scheduleItem
.ProgramScheduleItemGraphicsElements ?? [])
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = programScheduleItemGraphicsElement.GraphicsElementId
});
}
var enumeratorStates = new Dictionary<CollectionKey, CollectionEnumeratorState>();
foreach ((CollectionKey key, IMediaCollectionEnumerator enumerator) in collectionEnumerators)
{

14
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs

@ -107,7 +107,8 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche @@ -107,7 +107,8 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode,
PlayoutItemWatermarks = []
PlayoutItemWatermarks = [],
PlayoutItemGraphicsElements = []
};
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
@ -121,6 +122,17 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche @@ -121,6 +122,17 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche
});
}
foreach (ProgramScheduleItemGraphicsElement programScheduleItemGraphicsElement in scheduleItem
.ProgramScheduleItemGraphicsElements ?? [])
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = programScheduleItemGraphicsElement.GraphicsElementId
});
}
// LogScheduledItem(scheduleItem, mediaItem, itemStartTime);
playoutItems.AddRange(

14
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs

@ -55,7 +55,8 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI @@ -55,7 +55,8 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode,
PlayoutItemWatermarks = []
PlayoutItemWatermarks = [],
PlayoutItemGraphicsElements = []
};
foreach (ProgramScheduleItemWatermark programScheduleItemWatermark in scheduleItem
@ -69,6 +70,17 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI @@ -69,6 +70,17 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI
});
}
foreach (ProgramScheduleItemGraphicsElement programScheduleItemGraphicsElement in scheduleItem
.ProgramScheduleItemGraphicsElements ?? [])
{
playoutItem.PlayoutItemGraphicsElements.Add(
new PlayoutItemGraphicsElement
{
PlayoutItem = playoutItem,
GraphicsElementId = programScheduleItemGraphicsElement.GraphicsElementId
});
}
List<PlayoutItem> playoutItems = AddFiller(
playoutBuilderState,
collectionEnumerators,

6366
ErsatzTV.Infrastructure.MySql/Migrations/20250905114217_Add_ProgramScheduleItemGraphicsElements.Designer.cs generated

File diff suppressed because it is too large Load Diff

51
ErsatzTV.Infrastructure.MySql/Migrations/20250905114217_Add_ProgramScheduleItemGraphicsElements.cs

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_ProgramScheduleItemGraphicsElements : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ProgramScheduleItemGraphicsElement",
columns: table => new
{
ProgramScheduleItemId = table.Column<int>(type: "int", nullable: false),
GraphicsElementId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProgramScheduleItemGraphicsElement", x => new { x.ProgramScheduleItemId, x.GraphicsElementId });
table.ForeignKey(
name: "FK_ProgramScheduleItemGraphicsElement_GraphicsElement_GraphicsE~",
column: x => x.GraphicsElementId,
principalTable: "GraphicsElement",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ProgramScheduleItemGraphicsElement_ProgramScheduleItem_Progr~",
column: x => x.ProgramScheduleItemId,
principalTable: "ProgramScheduleItem",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_ProgramScheduleItemGraphicsElement_GraphicsElementId",
table: "ProgramScheduleItemGraphicsElement",
column: "GraphicsElementId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ProgramScheduleItemGraphicsElement");
}
}
}

38
ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs

@ -2284,6 +2284,21 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -2284,6 +2284,21 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.UseTptMappingStrategy();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemGraphicsElement", b =>
{
b.Property<int>("ProgramScheduleItemId")
.HasColumnType("int");
b.Property<int>("GraphicsElementId")
.HasColumnType("int");
b.HasKey("ProgramScheduleItemId", "GraphicsElementId");
b.HasIndex("GraphicsElementId");
b.ToTable("ProgramScheduleItemGraphicsElement");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemWatermark", b =>
{
b.Property<int>("ProgramScheduleItemId")
@ -4973,6 +4988,25 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -4973,6 +4988,25 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Navigation("TailFiller");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemGraphicsElement", b =>
{
b.HasOne("ErsatzTV.Core.Domain.GraphicsElement", "GraphicsElement")
.WithMany("ProgramScheduleItemGraphicsElements")
.HasForeignKey("GraphicsElementId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "ProgramScheduleItem")
.WithMany("ProgramScheduleItemGraphicsElements")
.HasForeignKey("ProgramScheduleItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GraphicsElement");
b.Navigation("ProgramScheduleItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemWatermark", b =>
{
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "ProgramScheduleItem")
@ -5909,6 +5943,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -5909,6 +5943,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
modelBuilder.Entity("ErsatzTV.Core.Domain.GraphicsElement", b =>
{
b.Navigation("PlayoutItemGraphicsElements");
b.Navigation("ProgramScheduleItemGraphicsElements");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ImageMetadata", b =>
@ -6083,6 +6119,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -6083,6 +6119,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
{
b.Navigation("ProgramScheduleItemGraphicsElements");
b.Navigation("ProgramScheduleItemWatermarks");
});

6201
ErsatzTV.Infrastructure.Sqlite/Migrations/20250905114029_Add_ProgramScheduleItemGraphicsElements.Designer.cs generated

File diff suppressed because it is too large Load Diff

50
ErsatzTV.Infrastructure.Sqlite/Migrations/20250905114029_Add_ProgramScheduleItemGraphicsElements.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_ProgramScheduleItemGraphicsElements : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ProgramScheduleItemGraphicsElement",
columns: table => new
{
ProgramScheduleItemId = table.Column<int>(type: "INTEGER", nullable: false),
GraphicsElementId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProgramScheduleItemGraphicsElement", x => new { x.ProgramScheduleItemId, x.GraphicsElementId });
table.ForeignKey(
name: "FK_ProgramScheduleItemGraphicsElement_GraphicsElement_GraphicsElementId",
column: x => x.GraphicsElementId,
principalTable: "GraphicsElement",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ProgramScheduleItemGraphicsElement_ProgramScheduleItem_ProgramScheduleItemId",
column: x => x.ProgramScheduleItemId,
principalTable: "ProgramScheduleItem",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ProgramScheduleItemGraphicsElement_GraphicsElementId",
table: "ProgramScheduleItemGraphicsElement",
column: "GraphicsElementId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ProgramScheduleItemGraphicsElement");
}
}
}

38
ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs

@ -2177,6 +2177,21 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -2177,6 +2177,21 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.UseTptMappingStrategy();
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemGraphicsElement", b =>
{
b.Property<int>("ProgramScheduleItemId")
.HasColumnType("INTEGER");
b.Property<int>("GraphicsElementId")
.HasColumnType("INTEGER");
b.HasKey("ProgramScheduleItemId", "GraphicsElementId");
b.HasIndex("GraphicsElementId");
b.ToTable("ProgramScheduleItemGraphicsElement");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemWatermark", b =>
{
b.Property<int>("ProgramScheduleItemId")
@ -4808,6 +4823,25 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -4808,6 +4823,25 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Navigation("TailFiller");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemGraphicsElement", b =>
{
b.HasOne("ErsatzTV.Core.Domain.GraphicsElement", "GraphicsElement")
.WithMany("ProgramScheduleItemGraphicsElements")
.HasForeignKey("GraphicsElementId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "ProgramScheduleItem")
.WithMany("ProgramScheduleItemGraphicsElements")
.HasForeignKey("ProgramScheduleItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GraphicsElement");
b.Navigation("ProgramScheduleItem");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemWatermark", b =>
{
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "ProgramScheduleItem")
@ -5744,6 +5778,8 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -5744,6 +5778,8 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
modelBuilder.Entity("ErsatzTV.Core.Domain.GraphicsElement", b =>
{
b.Navigation("PlayoutItemGraphicsElements");
b.Navigation("ProgramScheduleItemGraphicsElements");
});
modelBuilder.Entity("ErsatzTV.Core.Domain.ImageMetadata", b =>
@ -5918,6 +5954,8 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -5918,6 +5954,8 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
{
b.Navigation("ProgramScheduleItemGraphicsElements");
b.Navigation("ProgramScheduleItemWatermarks");
});

13
ErsatzTV.Infrastructure/Data/Configurations/ProgramScheduleItemConfiguration.cs

@ -82,5 +82,18 @@ public class ProgramScheduleItemConfiguration : IEntityTypeConfiguration<Program @@ -82,5 +82,18 @@ public class ProgramScheduleItemConfiguration : IEntityTypeConfiguration<Program
.HasForeignKey(ci => ci.ProgramScheduleItemId)
.OnDelete(DeleteBehavior.Cascade),
j => j.HasKey(ci => new { ci.ProgramScheduleItemId, ci.WatermarkId }));
builder.HasMany(c => c.GraphicsElements)
.WithMany(m => m.ProgramScheduleItems)
.UsingEntity<ProgramScheduleItemGraphicsElement>(
j => j.HasOne(ci => ci.GraphicsElement)
.WithMany(mi => mi.ProgramScheduleItemGraphicsElements)
.HasForeignKey(ci => ci.GraphicsElementId)
.OnDelete(DeleteBehavior.Cascade),
j => j.HasOne(ci => ci.ProgramScheduleItem)
.WithMany(c => c.ProgramScheduleItemGraphicsElements)
.HasForeignKey(ci => ci.ProgramScheduleItemId)
.OnDelete(DeleteBehavior.Cascade),
j => j.HasKey(ci => new { ci.ProgramScheduleItemId, ci.GraphicsElementId }));
}
}

24
ErsatzTV/Pages/ScheduleItemsEditor.razor

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
@page "/schedules/{Id:int}/items"
@using ErsatzTV.Application.Filler
@using ErsatzTV.Application.Graphics
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaItems
@using ErsatzTV.Application.ProgramSchedules
@ -574,6 +575,21 @@ @@ -574,6 +575,21 @@
}
</MudSelect>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Graphics Elements</MudText>
</div>
<MudSelect T="GraphicsElementViewModel"
@bind-SelectedValues="_selectedItem.GraphicsElements"
ToStringFunc="@(ge => ge?.Name)"
Clearable="true"
MultiSelection="true">
@foreach (GraphicsElementViewModel graphicsElement in _graphicsElements)
{
<MudSelectItem Value="@graphicsElement">@graphicsElement.Name</MudSelectItem>
}
</MudSelect>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Preferred Audio Language</MudText>
@ -633,6 +649,7 @@ @@ -633,6 +649,7 @@
private ProgramScheduleItemsEditViewModel _schedule = new();
private List<FillerPresetViewModel> _fillerPresets = [];
private List<WatermarkViewModel> _watermarks = [];
private List<GraphicsElementViewModel> _graphicsElements = [];
private List<LanguageCodeViewModel> _availableCultures = [];
private readonly List<PlaylistGroupViewModel> _playlistGroups = [];
private readonly List<PlaylistViewModel> _playlists = [];
@ -663,6 +680,8 @@ @@ -663,6 +680,8 @@
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_watermarks = await Mediator.Send(new GetAllWatermarks(), token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_graphicsElements = await Mediator.Send(new GetAllGraphicsElements(), token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_availableCultures = await Mediator.Send(new GetAllLanguageCodes(), token);
_playlistGroups.AddRange(await Mediator.Send(new GetAllPlaylistGroups(), token));
@ -795,7 +814,8 @@ @@ -795,7 +814,8 @@
PreferredAudioTitle = item.PreferredAudioTitle,
PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode,
SubtitleMode = item.SubtitleMode,
Watermarks = item.Watermarks
Watermarks = item.Watermarks,
GraphicsElements = item.GraphicsElements
};
switch (item)
@ -861,6 +881,7 @@ @@ -861,6 +881,7 @@
TailFiller = item.TailFiller,
FallbackFiller = item.FallbackFiller,
Watermarks = item.Watermarks.ToList(),
GraphicsElements = item.GraphicsElements.ToList(),
PreferredAudioLanguageCode = item.PreferredAudioLanguageCode,
PreferredAudioTitle = item.PreferredAudioTitle,
PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode,
@ -935,6 +956,7 @@ @@ -935,6 +956,7 @@
item.TailFiller?.Id,
item.FallbackFiller?.Id,
item.Watermarks.Map(wm => wm.Id).ToList(),
item.GraphicsElements.Map(wm => wm.Id).ToList(),
item.PreferredAudioLanguageCode,
item.PreferredAudioTitle,
item.PreferredSubtitleLanguageCode,

2
ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
using System.Runtime.CompilerServices;
using ErsatzTV.Annotations;
using ErsatzTV.Application.Filler;
using ErsatzTV.Application.Graphics;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Application.Watermarks;
@ -93,6 +94,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged @@ -93,6 +94,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged
public FillerPresetViewModel TailFiller { get; set; }
public FillerPresetViewModel FallbackFiller { get; set; }
public IEnumerable<WatermarkViewModel> Watermarks { get; set; }
public IEnumerable<GraphicsElementViewModel> GraphicsElements { get; set; }
public string PreferredAudioLanguageCode { get; set; }
public string PreferredAudioTitle { get; set; }
public string PreferredSubtitleLanguageCode { get; set; }

Loading…
Cancel
Save