Browse Source

evenly divide epg time for schedule blocks (#1607)

* add checkbox to include block items in program guide

* evenly divide epg time for schedule blocks
pull/1609/head
Jason Dove 1 year ago committed by GitHub
parent
commit
d55ba235bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 278
      ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs
  2. 3
      ErsatzTV.Application/Scheduling/BlockItemViewModel.cs
  3. 3
      ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItem.cs
  4. 3
      ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItemsHandler.cs
  5. 3
      ErsatzTV.Application/Scheduling/Mapper.cs
  6. 16
      ErsatzTV.Application/Scheduling/Queries/GetBlockItemsHandler.cs
  7. 4
      ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutChangeDetectionTests.cs
  8. 1
      ErsatzTV.Core/Domain/PlayoutItem.cs
  9. 1
      ErsatzTV.Core/Domain/Scheduling/BlockItem.cs
  10. 8
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs
  11. 20
      ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs
  12. 4928
      ErsatzTV.Infrastructure.MySql/Migrations/20240210152747_Add_BlockItem_IncludeInProgramGuide.Designer.cs
  13. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20240210152747_Add_BlockItem_IncludeInProgramGuide.cs
  14. 4931
      ErsatzTV.Infrastructure.MySql/Migrations/20240211021432_Add_PlayoutItem_GuideStart.Designer.cs
  15. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20240211021432_Add_PlayoutItem_GuideStart.cs
  16. 6
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  17. 4926
      ErsatzTV.Infrastructure.Sqlite/Migrations/20240210151215_Add_BlockItem_IncludeInProgramGuide.Designer.cs
  18. 29
      ErsatzTV.Infrastructure.Sqlite/Migrations/20240210151215_Add_BlockItem_IncludeInProgramGuide.cs
  19. 4929
      ErsatzTV.Infrastructure.Sqlite/Migrations/20240211014633_Add_PlayoutItem_GuideStart.Designer.cs
  20. 29
      ErsatzTV.Infrastructure.Sqlite/Migrations/20240211014633_Add_PlayoutItem_GuideStart.cs
  21. 6
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  22. 1
      ErsatzTV.sln.DotSettings
  23. 19
      ErsatzTV/Pages/BlockEditor.razor
  24. 2
      ErsatzTV/ViewModels/BlockItemEditViewModel.cs

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

@ -159,27 +159,81 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData> @@ -159,27 +159,81 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
.ThenInclude(sm => sm.Studios)
.ToListAsync(cancellationToken);
List<PlayoutItem> sorted = [];
await using RecyclableMemoryStream ms = _recyclableMemoryStreamManager.GetStream();
await using var xml = XmlWriter.Create(
ms,
new XmlWriterSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment });
foreach (Playout playout in playouts)
{
switch (playout.ProgramSchedulePlayoutType)
{
case ProgramSchedulePlayoutType.Flood:
var floodSorted = playouts.Collect(p => p.Items).OrderBy(pi => pi.Start).ToList();
await WritePlayoutXml(
request,
floodSorted,
templateContext,
movieTemplate,
episodeTemplate,
musicVideoTemplate,
songTemplate,
otherVideoTemplate,
minifier,
xml);
break;
case ProgramSchedulePlayoutType.Block:
sorted.AddRange(playouts.Collect(p => p.Items).OrderBy(pi => pi.Start));
var blockSorted = playouts.Collect(p => p.Items).OrderBy(pi => pi.Start).ToList();
await WriteBlockPlayoutXml(
request,
blockSorted,
templateContext,
movieTemplate,
episodeTemplate,
musicVideoTemplate,
songTemplate,
otherVideoTemplate,
minifier,
xml);
break;
case ProgramSchedulePlayoutType.ExternalJson:
sorted.AddRange(await CollectExternalJsonItems(playout.ExternalJsonFile));
List<PlayoutItem> externalJsonSorted = await CollectExternalJsonItems(playout.ExternalJsonFile);
await WritePlayoutXml(
request,
externalJsonSorted,
templateContext,
movieTemplate,
episodeTemplate,
musicVideoTemplate,
songTemplate,
otherVideoTemplate,
minifier,
xml);
break;
}
}
await using RecyclableMemoryStream ms = _recyclableMemoryStreamManager.GetStream();
await using var xml = XmlWriter.Create(
ms,
new XmlWriterSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment });
await xml.FlushAsync();
string tempFile = Path.GetTempFileName();
await File.WriteAllBytesAsync(tempFile, ms.ToArray(), cancellationToken);
string targetFile = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{request.ChannelNumber}.xml");
File.Move(tempFile, targetFile, true);
}
private async Task WritePlayoutXml(
RefreshChannelData request,
List<PlayoutItem> sorted,
XmlTemplateContext templateContext,
Template movieTemplate,
Template episodeTemplate,
Template musicVideoTemplate,
Template songTemplate,
Template otherVideoTemplate,
XmlMinifier minifier,
XmlWriter xml)
{
// skip all filler that isn't pre-roll
var i = 0;
while (i < sorted.Count && sorted[i].FillerKind != FillerKind.None &&
@ -232,83 +286,159 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData> @@ -232,83 +286,159 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
: finishItem.FinishOffset.ToString("yyyyMMddHHmmss zzz", CultureInfo.InvariantCulture)
.Replace(":", string.Empty);
string title = GetTitle(displayItem);
string subtitle = GetSubtitle(displayItem);
await WriteItemToXml(
request,
displayItem,
start,
stop,
hasCustomTitle,
templateContext,
movieTemplate,
episodeTemplate,
musicVideoTemplate,
songTemplate,
otherVideoTemplate,
minifier,
xml);
i++;
}
}
private async Task WriteBlockPlayoutXml(
RefreshChannelData request,
List<PlayoutItem> sorted,
XmlTemplateContext templateContext,
Template movieTemplate,
Template episodeTemplate,
Template musicVideoTemplate,
Template songTemplate,
Template otherVideoTemplate,
XmlMinifier minifier,
XmlWriter xml)
{
var groups = sorted.GroupBy(s => new { s.GuideStart, s.GuideFinish, s.GuideGroup });
foreach (var group in groups)
{
DateTime groupStart = group.Key.GuideStart!.Value;
DateTime groupFinish = group.Key.GuideFinish!.Value;
TimeSpan groupDuration = groupFinish - groupStart;
var itemsToInclude = group.Filter(g => g.FillerKind is FillerKind.None).ToList();
TimeSpan perItem = groupDuration / itemsToInclude.Count;
DateTimeOffset currentStart = new DateTimeOffset(groupStart, TimeSpan.Zero).ToLocalTime();
DateTimeOffset currentFinish = currentStart + perItem;
Option<string> maybeTemplateOutput = displayItem.MediaItem switch
foreach (PlayoutItem item in itemsToInclude)
{
Movie templateMovie => await ProcessMovieTemplate(
request,
templateMovie,
start,
stop,
hasCustomTitle,
displayItem,
title,
templateContext,
movieTemplate),
Episode templateEpisode => await ProcessEpisodeTemplate(
request,
templateEpisode,
start,
stop,
hasCustomTitle,
displayItem,
title,
subtitle,
templateContext,
episodeTemplate),
MusicVideo templateMusicVideo => await ProcessMusicVideoTemplate(
request,
templateMusicVideo,
start,
stop,
hasCustomTitle,
displayItem,
title,
subtitle,
templateContext,
musicVideoTemplate),
Song templateSong => await ProcessSongTemplate(
request,
templateSong,
start,
stop,
hasCustomTitle,
displayItem,
title,
subtitle,
templateContext,
songTemplate),
OtherVideo templateOtherVideo => await ProcessOtherVideoTemplate(
string start = currentStart.ToString("yyyyMMddHHmmss zzz", CultureInfo.InvariantCulture)
.Replace(":", string.Empty);
string stop = currentFinish.ToString("yyyyMMddHHmmss zzz", CultureInfo.InvariantCulture)
.Replace(":", string.Empty);
await WriteItemToXml(
request,
templateOtherVideo,
item,
start,
stop,
hasCustomTitle,
displayItem,
title,
hasCustomTitle: false,
templateContext,
otherVideoTemplate),
_ => Option<string>.None
};
foreach (string templateOutput in maybeTemplateOutput)
{
MarkupMinificationResult minified = minifier.Minify(templateOutput);
await xml.WriteRawAsync(minified.MinifiedContent);
movieTemplate,
episodeTemplate,
musicVideoTemplate,
songTemplate,
otherVideoTemplate,
minifier,
xml);
currentStart = currentFinish;
currentFinish += perItem;
}
i++;
}
}
await xml.FlushAsync();
private async Task WriteItemToXml(
RefreshChannelData request,
PlayoutItem displayItem,
string start,
string stop,
bool hasCustomTitle,
XmlTemplateContext templateContext,
Template movieTemplate,
Template episodeTemplate,
Template musicVideoTemplate,
Template songTemplate,
Template otherVideoTemplate,
XmlMinifier minifier,
XmlWriter xml)
{
string title = GetTitle(displayItem);
string subtitle = GetSubtitle(displayItem);
string tempFile = Path.GetTempFileName();
await File.WriteAllBytesAsync(tempFile, ms.ToArray(), cancellationToken);
Option<string> maybeTemplateOutput = displayItem.MediaItem switch
{
Movie templateMovie => await ProcessMovieTemplate(
request,
templateMovie,
start,
stop,
hasCustomTitle,
displayItem,
title,
templateContext,
movieTemplate),
Episode templateEpisode => await ProcessEpisodeTemplate(
request,
templateEpisode,
start,
stop,
hasCustomTitle,
displayItem,
title,
subtitle,
templateContext,
episodeTemplate),
MusicVideo templateMusicVideo => await ProcessMusicVideoTemplate(
request,
templateMusicVideo,
start,
stop,
hasCustomTitle,
displayItem,
title,
subtitle,
templateContext,
musicVideoTemplate),
Song templateSong => await ProcessSongTemplate(
request,
templateSong,
start,
stop,
hasCustomTitle,
displayItem,
title,
subtitle,
templateContext,
songTemplate),
OtherVideo templateOtherVideo => await ProcessOtherVideoTemplate(
request,
templateOtherVideo,
start,
stop,
hasCustomTitle,
displayItem,
title,
templateContext,
otherVideoTemplate),
_ => Option<string>.None
};
string targetFile = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{request.ChannelNumber}.xml");
File.Move(tempFile, targetFile, true);
foreach (string templateOutput in maybeTemplateOutput)
{
MarkupMinificationResult minified = minifier.Minify(templateOutput);
await xml.WriteRawAsync(minified.MinifiedContent);
}
}
private static async Task<Option<string>> ProcessMovieTemplate(

3
ErsatzTV.Application/Scheduling/BlockItemViewModel.cs

@ -12,4 +12,5 @@ public record BlockItemViewModel( @@ -12,4 +12,5 @@ public record BlockItemViewModel(
MultiCollectionViewModel MultiCollection,
SmartCollectionViewModel SmartCollection,
NamedMediaItemViewModel MediaItem,
PlaybackOrder PlaybackOrder);
PlaybackOrder PlaybackOrder,
bool IncludeInProgramGuide);

3
ErsatzTV.Application/Scheduling/Commands/ReplaceBlockItem.cs

@ -9,4 +9,5 @@ public record ReplaceBlockItem( @@ -9,4 +9,5 @@ public record ReplaceBlockItem(
int? MultiCollectionId,
int? SmartCollectionId,
int? MediaItemId,
PlaybackOrder PlaybackOrder);
PlaybackOrder PlaybackOrder,
bool IncludeInProgramGuide);

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

@ -53,7 +53,8 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact @@ -53,7 +53,8 @@ public class ReplaceBlockItemsHandler(IDbContextFactory<TvContext> dbContextFact
MultiCollectionId = item.MultiCollectionId,
SmartCollectionId = item.SmartCollectionId,
MediaItemId = item.MediaItemId,
PlaybackOrder = item.PlaybackOrder
PlaybackOrder = item.PlaybackOrder,
IncludeInProgramGuide = item.IncludeInProgramGuide
};
private static Task<Validation<BaseError, Block>> Validate(TvContext dbContext, ReplaceBlockItems request) =>

3
ErsatzTV.Application/Scheduling/Mapper.cs

@ -30,7 +30,8 @@ internal static class Mapper @@ -30,7 +30,8 @@ internal static class Mapper
Artist artist => MediaItems.Mapper.ProjectToViewModel(artist),
_ => null
},
blockItem.PlaybackOrder);
blockItem.PlaybackOrder,
blockItem.IncludeInProgramGuide);
internal static TemplateGroupViewModel ProjectToViewModel(TemplateGroup templateGroup) =>
new(templateGroup.Id, templateGroup.Name, templateGroup.Templates.Count);

16
ErsatzTV.Application/Scheduling/Queries/GetBlockItemsHandler.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Scheduling;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
@ -11,7 +12,7 @@ public class GetBlockItemsHandler(IDbContextFactory<TvContext> dbContextFactory) @@ -11,7 +12,7 @@ public class GetBlockItemsHandler(IDbContextFactory<TvContext> dbContextFactory)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
return await dbContext.BlockItems
List<BlockItem> allItems = await dbContext.BlockItems
.AsNoTracking()
.Filter(i => i.BlockId == request.BlockId)
.Include(i => i.Collection)
@ -30,7 +31,16 @@ public class GetBlockItemsHandler(IDbContextFactory<TvContext> dbContextFactory) @@ -30,7 +31,16 @@ public class GetBlockItemsHandler(IDbContextFactory<TvContext> dbContextFactory)
.Include(i => i.MediaItem)
.ThenInclude(i => (i as Artist).ArtistMetadata)
.ThenInclude(am => am.Artwork)
.ToListAsync(cancellationToken)
.Map(items => items.Map(Mapper.ProjectToViewModel).ToList());
.ToListAsync(cancellationToken);
if (allItems.All(bi => bi.IncludeInProgramGuide == false))
{
foreach (BlockItem bi in allItems)
{
bi.IncludeInProgramGuide = true;
}
}
return allItems.Map(Mapper.ProjectToViewModel).ToList();
}
}

4
ErsatzTV.Core.Tests/Scheduling/BlockScheduling/BlockPlayoutChangeDetectionTests.cs

@ -64,8 +64,8 @@ public static class BlockPlayoutChangeDetectionTests @@ -64,8 +64,8 @@ public static class BlockPlayoutChangeDetectionTests
List<EffectiveBlock> effectiveBlocks =
[
new EffectiveBlock(block1, blockKey1, GetLocalDate(2024, 1, 17).AddHours(9)),
new EffectiveBlock(block2, blockKey2, GetLocalDate(2024, 1, 17).AddHours(13)),
new EffectiveBlock(block1, blockKey1, GetLocalDate(2024, 1, 17).AddHours(9), 1),
new EffectiveBlock(block2, blockKey2, GetLocalDate(2024, 1, 17).AddHours(13), 2),
];
Map<CollectionKey, string> collectionEtags = LanguageExt.Map<CollectionKey, string>.Empty;

1
ErsatzTV.Core/Domain/PlayoutItem.cs

@ -11,6 +11,7 @@ public class PlayoutItem @@ -11,6 +11,7 @@ public class PlayoutItem
public MediaItem MediaItem { get; set; }
public DateTime Start { get; set; }
public DateTime Finish { get; set; }
public DateTime? GuideStart { get; set; }
public DateTime? GuideFinish { get; set; }
public string CustomTitle { get; set; }
public int GuideGroup { get; set; }

1
ErsatzTV.Core/Domain/Scheduling/BlockItem.cs

@ -16,4 +16,5 @@ public class BlockItem @@ -16,4 +16,5 @@ public class BlockItem
public int? SmartCollectionId { get; set; }
public SmartCollection SmartCollection { get; set; }
public PlaybackOrder PlaybackOrder { get; set; }
public bool IncludeInProgramGuide { get; set; }
}

8
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs

@ -32,8 +32,6 @@ public class BlockPlayoutBuilder( @@ -32,8 +32,6 @@ public class BlockPlayoutBuilder(
playout.Channel.Number,
playout.Channel.Name);
var random = new Random();
List<PlaybackOrder> allowedPlaybackOrders =
[
PlaybackOrder.Chronological,
@ -149,14 +147,16 @@ public class BlockPlayoutBuilder( @@ -149,14 +147,16 @@ public class BlockPlayoutBuilder(
Finish = currentTime.UtcDateTime + itemDuration,
InPoint = TimeSpan.Zero,
OutPoint = itemDuration,
FillerKind = FillerKind.None,
FillerKind = blockItem.IncludeInProgramGuide ? FillerKind.None : FillerKind.GuideMode,
//CustomTitle = scheduleItem.CustomTitle,
//WatermarkId = scheduleItem.WatermarkId,
//PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode,
//PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
//PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
//SubtitleMode = scheduleItem.SubtitleMode
GuideGroup = random.Next(),
GuideGroup = effectiveBlock.TemplateItemId,
GuideStart = effectiveBlock.Start.UtcDateTime,
GuideFinish = blockFinish.UtcDateTime,
BlockKey = JsonConvert.SerializeObject(effectiveBlock.BlockKey),
CollectionKey = JsonConvert.SerializeObject(collectionKey, JsonSettings),
CollectionEtag = collectionEtags[collectionKey]

20
ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs

@ -2,7 +2,7 @@ using ErsatzTV.Core.Domain.Scheduling; @@ -2,7 +2,7 @@ using ErsatzTV.Core.Domain.Scheduling;
namespace ErsatzTV.Core.Scheduling.BlockScheduling;
internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset Start)
internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset Start, int TemplateItemId)
{
public static List<EffectiveBlock> GetEffectiveBlocks(
ICollection<PlayoutTemplate> templates,
@ -27,6 +27,7 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St @@ -27,6 +27,7 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St
var newBlocks = playoutTemplate.Template.Items
.Map(i => ToEffectiveBlock(playoutTemplate, i, today, start))
.Map(NormalizeGuideMode)
.ToList();
effectiveBlocks.AddRange(newBlocks);
@ -56,5 +57,20 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St @@ -56,5 +57,20 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St
templateItem.StartTime.Hours,
templateItem.StartTime.Minutes,
0,
start.Offset));
start.Offset),
templateItem.Id);
private static EffectiveBlock NormalizeGuideMode(EffectiveBlock effectiveBlock)
{
if (effectiveBlock.Block.Items is not null &&
effectiveBlock.Block.Items.All(bi => bi.IncludeInProgramGuide == false))
{
foreach (BlockItem blockItem in effectiveBlock.Block.Items)
{
blockItem.IncludeInProgramGuide = true;
}
}
return effectiveBlock;
}
}

4928
ErsatzTV.Infrastructure.MySql/Migrations/20240210152747_Add_BlockItem_IncludeInProgramGuide.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20240210152747_Add_BlockItem_IncludeInProgramGuide.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_BlockItem_IncludeInProgramGuide : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IncludeInProgramGuide",
table: "BlockItem",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IncludeInProgramGuide",
table: "BlockItem");
}
}
}

4931
ErsatzTV.Infrastructure.MySql/Migrations/20240211021432_Add_PlayoutItem_GuideStart.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20240211021432_Add_PlayoutItem_GuideStart.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_PlayoutItem_GuideStart : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "GuideStart",
table: "PlayoutItem",
type: "datetime(6)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "GuideStart",
table: "PlayoutItem");
}
}
}

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

@ -1470,6 +1470,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -1470,6 +1470,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int>("GuideGroup")
.HasColumnType("int");
b.Property<DateTime?>("GuideStart")
.HasColumnType("datetime(6)");
b.Property<TimeSpan>("InPoint")
.HasColumnType("time(6)");
@ -1895,6 +1898,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -1895,6 +1898,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int>("CollectionType")
.HasColumnType("int");
b.Property<bool>("IncludeInProgramGuide")
.HasColumnType("tinyint(1)");
b.Property<int>("Index")
.HasColumnType("int");

4926
ErsatzTV.Infrastructure.Sqlite/Migrations/20240210151215_Add_BlockItem_IncludeInProgramGuide.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.Sqlite/Migrations/20240210151215_Add_BlockItem_IncludeInProgramGuide.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_BlockItem_IncludeInProgramGuide : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IncludeInProgramGuide",
table: "BlockItem",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IncludeInProgramGuide",
table: "BlockItem");
}
}
}

4929
ErsatzTV.Infrastructure.Sqlite/Migrations/20240211014633_Add_PlayoutItem_GuideStart.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.Sqlite/Migrations/20240211014633_Add_PlayoutItem_GuideStart.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_PlayoutItem_GuideStart : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "GuideStart",
table: "PlayoutItem",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "GuideStart",
table: "PlayoutItem");
}
}
}

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

@ -1468,6 +1468,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -1468,6 +1468,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int>("GuideGroup")
.HasColumnType("INTEGER");
b.Property<DateTime?>("GuideStart")
.HasColumnType("TEXT");
b.Property<TimeSpan>("InPoint")
.HasColumnType("TEXT");
@ -1893,6 +1896,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -1893,6 +1896,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int>("CollectionType")
.HasColumnType("INTEGER");
b.Property<bool>("IncludeInProgramGuide")
.HasColumnType("INTEGER");
b.Property<int>("Index")
.HasColumnType("INTEGER");

1
ErsatzTV.sln.DotSettings

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=ErsatzTV_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DTO/@EntryIndexedValue">DTO</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EPG/@EntryIndexedValue">EPG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FF/@EntryIndexedValue">FF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HDHR/@EntryIndexedValue">HDHR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LE/@EntryIndexedValue">LE</s:String>

19
ErsatzTV/Pages/BlockEditor.razor

@ -56,6 +56,7 @@ @@ -56,6 +56,7 @@
<MudItem xs="8">
<MudTable Class="mt-6" Hover="true" Items="_block.Items.OrderBy(i => i.Index)" Dense="true" @bind-SelectedItem="_selectedItem">
<ColGroup>
<col/>
<col/>
<col/>
<col style="width: 60px;"/>
@ -66,6 +67,7 @@ @@ -66,6 +67,7 @@
<HeaderContent>
<MudTh>Collection</MudTh>
<MudTh>Playback Order</MudTh>
<MudTh>Show In EPG</MudTh>
<MudTh/>
<MudTh/>
<MudTh/>
@ -82,6 +84,9 @@ @@ -82,6 +84,9 @@
@context.PlaybackOrder
</MudText>
</MudTd>
<MudTd>
<MudCheckBox T="bool" Value="@context.IncludeInProgramGuide" ValueChanged="@(e => UpdateEPG(context, e))" />
</MudTd>
<MudTd>
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy"
OnClick="@(_ => CopyItem(context))">
@ -390,7 +395,8 @@ @@ -390,7 +395,8 @@
MultiCollection = item.MultiCollection,
SmartCollection = item.SmartCollection,
MediaItem = item.MediaItem,
PlaybackOrder = item.PlaybackOrder
PlaybackOrder = item.PlaybackOrder,
IncludeInProgramGuide = item.IncludeInProgramGuide
};
private void AddBlockItem()
@ -416,7 +422,8 @@ @@ -416,7 +422,8 @@
Collection = item.Collection,
MultiCollection = item.MultiCollection,
SmartCollection = item.SmartCollection,
MediaItem = item.MediaItem
MediaItem = item.MediaItem,
IncludeInProgramGuide = item.IncludeInProgramGuide
};
foreach (BlockItemEditViewModel i in _block.Items.Filter(bi => bi.Index >= newItem.Index))
@ -473,7 +480,8 @@ @@ -473,7 +480,8 @@
item.MultiCollection?.Id,
item.SmartCollection?.Id,
item.MediaItem?.MediaItemId,
item.PlaybackOrder)).ToList();
item.PlaybackOrder,
item.IncludeInProgramGuide)).ToList();
_block.Minutes = _durationHours * 60 + _durationMinutes;
@ -485,4 +493,9 @@ @@ -485,4 +493,9 @@
_selectedItem = null;
_previewItems = await Mediator.Send(new PreviewBlockPlayout(GenerateReplaceRequest()), _cts.Token);
}
private static void UpdateEPG(BlockItemEditViewModel context, bool includeInProgramGuide)
{
context.IncludeInProgramGuide = includeInProgramGuide;
}
}

2
ErsatzTV/ViewModels/BlockItemEditViewModel.cs

@ -59,6 +59,8 @@ public class BlockItemEditViewModel : INotifyPropertyChanged @@ -59,6 +59,8 @@ public class BlockItemEditViewModel : INotifyPropertyChanged
};
public PlaybackOrder PlaybackOrder { get; set; }
public bool IncludeInProgramGuide { get; set; }
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{

Loading…
Cancel
Save