add playlist item play all option (#1694)
This commit is contained in:
+6
-1
@@ -50,8 +50,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Songs
|
||||
- Images
|
||||
- Playlists can be added to schedules as a schedule item
|
||||
- Each time through the playlist, one item will be scheduled from each playlist item
|
||||
- Each time through the playlist, one item will be scheduled from each playlist item (if `Play All` is unchecked)
|
||||
- NB: This does not mean every collection will always schedule one item; the normal flood playout restrictions like duration and fixed start times still apply here
|
||||
- If `Play All` is checked, that playlist item will play all of its items each time through the playlist
|
||||
- This can be helpful if you want to play entire collections in a specific order, e.g.
|
||||
- Every episode from Show 1 Season 2
|
||||
- Every episode from Show 2 Season 3
|
||||
- Every episode from Show 1 Season 3
|
||||
- Playlist items with fewer media items will be re-shuffled (if applicable) before those with more media items
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using ErsatzTV.Application.Scheduling;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Domain.Scheduling;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Interfaces.Scheduling;
|
||||
using ErsatzTV.Core.Scheduling;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
@@ -13,7 +13,8 @@ namespace ErsatzTV.Application.MediaCollections;
|
||||
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")]
|
||||
public class PreviewPlaylistPlayoutHandler(
|
||||
IDbContextFactory<TvContext> dbContextFactory,
|
||||
IBlockPlayoutPreviewBuilder blockPlayoutBuilder)
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
IPlayoutBuilder playoutBuilder)
|
||||
: IRequestHandler<PreviewPlaylistPlayout, List<PlayoutItemPreviewViewModel>>
|
||||
{
|
||||
public async Task<List<PlayoutItemPreviewViewModel>> Handle(
|
||||
@@ -22,21 +23,6 @@ public class PreviewPlaylistPlayoutHandler(
|
||||
{
|
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
// TODO: consider using flood playout instead
|
||||
|
||||
var template = new Template
|
||||
{
|
||||
Items = []
|
||||
};
|
||||
|
||||
template.Items.Add(
|
||||
new TemplateItem
|
||||
{
|
||||
Block = MapToBlock(request.Data),
|
||||
StartTime = TimeSpan.Zero,
|
||||
Template = template
|
||||
});
|
||||
|
||||
var playout = new Playout
|
||||
{
|
||||
Channel = new Channel(Guid.NewGuid())
|
||||
@@ -45,21 +31,38 @@ public class PreviewPlaylistPlayoutHandler(
|
||||
Name = "Playlist Preview"
|
||||
},
|
||||
Items = [],
|
||||
ProgramSchedulePlayoutType = ProgramSchedulePlayoutType.Block,
|
||||
ProgramSchedulePlayoutType = ProgramSchedulePlayoutType.Flood,
|
||||
PlayoutHistory = [],
|
||||
Templates =
|
||||
[
|
||||
new PlayoutTemplate
|
||||
ProgramSchedule = new ProgramSchedule
|
||||
{
|
||||
DaysOfWeek = PlayoutTemplate.AllDaysOfWeek(),
|
||||
DaysOfMonth = PlayoutTemplate.AllDaysOfMonth(),
|
||||
MonthsOfYear = PlayoutTemplate.AllMonthsOfYear(),
|
||||
Template = template
|
||||
}
|
||||
]
|
||||
Items = [MapToScheduleItem(request)]
|
||||
},
|
||||
ProgramScheduleAlternates = [],
|
||||
FillGroupIndices = []
|
||||
};
|
||||
|
||||
await blockPlayoutBuilder.Build(playout, PlayoutBuildMode.Reset, cancellationToken);
|
||||
// TODO: make an explicit method to preview, this is ugly
|
||||
playoutBuilder.TrimStart = false;
|
||||
playoutBuilder.DebugPlaylist = playout.ProgramSchedule.Items[0].Playlist;
|
||||
await playoutBuilder.Build(playout, PlayoutBuildMode.Reset, cancellationToken);
|
||||
|
||||
int maxItems = 0;
|
||||
Dictionary<PlaylistItem, List<MediaItem>> map =
|
||||
await mediaCollectionRepository.GetPlaylistItemMap(playout.ProgramSchedule.Items[0].Playlist);
|
||||
foreach (PlaylistItem item in playout.ProgramSchedule.Items[0].Playlist.Items)
|
||||
{
|
||||
if (item.PlayAll)
|
||||
{
|
||||
maxItems += map[item].Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxItems += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// limit preview to once through the playlist
|
||||
playout.Items = playout.Items.Take(maxItems).ToList();
|
||||
|
||||
// load playout item details for title
|
||||
foreach (PlayoutItem playoutItem in playout.Items)
|
||||
@@ -92,28 +95,31 @@ public class PreviewPlaylistPlayoutHandler(
|
||||
}
|
||||
}
|
||||
|
||||
return playout.Items.Map(Scheduling.Mapper.ProjectToViewModel).ToList();
|
||||
return playout.Items.OrderBy(i => i.StartOffset).Map(Scheduling.Mapper.ProjectToViewModel).ToList();
|
||||
}
|
||||
|
||||
private static Block MapToBlock(ReplacePlaylistItems request) =>
|
||||
private static ProgramScheduleItemFlood MapToScheduleItem(PreviewPlaylistPlayout request) =>
|
||||
new()
|
||||
{
|
||||
Name = request.Name,
|
||||
Minutes = 6 * 60,
|
||||
StopScheduling = BlockStopScheduling.AfterDurationEnd,
|
||||
Items = request.Items.Map(MapToBlockItem).ToList()
|
||||
CollectionType = ProgramScheduleItemCollectionType.Playlist,
|
||||
Playlist = new Playlist
|
||||
{
|
||||
Items = request.Data.Items.OrderBy(i => i.Index).Map(MapToPlaylistItem).ToList()
|
||||
},
|
||||
PlaylistId = request.Data.PlaylistId,
|
||||
PlaybackOrder = PlaybackOrder.Shuffle
|
||||
};
|
||||
|
||||
private static BlockItem MapToBlockItem(int id, ReplacePlaylistItem request) =>
|
||||
private static PlaylistItem MapToPlaylistItem(ReplacePlaylistItem item) =>
|
||||
new()
|
||||
{
|
||||
Id = id,
|
||||
Index = request.Index,
|
||||
CollectionType = request.CollectionType,
|
||||
CollectionId = request.CollectionId,
|
||||
MultiCollectionId = request.MultiCollectionId,
|
||||
SmartCollectionId = request.SmartCollectionId,
|
||||
MediaItemId = request.MediaItemId,
|
||||
PlaybackOrder = request.PlaybackOrder
|
||||
Index = item.Index,
|
||||
CollectionType = item.CollectionType,
|
||||
CollectionId = item.CollectionId,
|
||||
MediaItemId = item.MediaItemId,
|
||||
MultiCollectionId = item.MultiCollectionId,
|
||||
SmartCollectionId = item.SmartCollectionId,
|
||||
PlaybackOrder = item.PlaybackOrder,
|
||||
PlayAll = item.PlayAll
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,4 +10,5 @@ public record ReplacePlaylistItem(
|
||||
int? SmartCollectionId,
|
||||
int? MediaItemId,
|
||||
PlaybackOrder PlaybackOrder,
|
||||
bool PlayAll,
|
||||
bool IncludeInProgramGuide);
|
||||
|
||||
@@ -46,6 +46,7 @@ public class ReplacePlaylistItemsHandler(IDbContextFactory<TvContext> dbContextF
|
||||
SmartCollectionId = item.SmartCollectionId,
|
||||
MediaItemId = item.MediaItemId,
|
||||
PlaybackOrder = item.PlaybackOrder,
|
||||
PlayAll = item.PlayAll,
|
||||
IncludeInProgramGuide = item.IncludeInProgramGuide
|
||||
};
|
||||
|
||||
|
||||
@@ -78,5 +78,6 @@ internal static class Mapper
|
||||
_ => null
|
||||
},
|
||||
playlistItem.PlaybackOrder,
|
||||
playlistItem.PlayAll,
|
||||
playlistItem.IncludeInProgramGuide);
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ public record PlaylistItemViewModel(
|
||||
SmartCollectionViewModel SmartCollection,
|
||||
NamedMediaItemViewModel MediaItem,
|
||||
PlaybackOrder PlaybackOrder,
|
||||
bool PlayAll,
|
||||
bool IncludeInProgramGuide);
|
||||
|
||||
@@ -13,6 +13,9 @@ public class FakeMediaCollectionRepository : IMediaCollectionRepository
|
||||
public Task<Dictionary<PlaylistItem, List<MediaItem>>> GetPlaylistItemMap(int playlistId) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public Task<Dictionary<PlaylistItem, List<MediaItem>>> GetPlaylistItemMap(Playlist playlist) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public Task<Option<Collection>> GetCollectionWithCollectionItemsUntracked(int id) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
|
||||
@@ -16,5 +16,6 @@ public class PlaylistItem
|
||||
public int? SmartCollectionId { get; set; }
|
||||
public SmartCollection SmartCollection { get; set; }
|
||||
public PlaybackOrder PlaybackOrder { get; set; }
|
||||
public bool PlayAll { get; set; }
|
||||
public bool IncludeInProgramGuide { get; set; }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories;
|
||||
public interface IMediaCollectionRepository
|
||||
{
|
||||
Task<Dictionary<PlaylistItem, List<MediaItem>>> GetPlaylistItemMap(int playlistId);
|
||||
Task<Dictionary<PlaylistItem, List<MediaItem>>> GetPlaylistItemMap(Playlist playlist);
|
||||
Task<Option<Collection>> GetCollectionWithCollectionItemsUntracked(int id);
|
||||
Task<List<MediaItem>> GetItems(int id);
|
||||
Task<List<MediaItem>> GetMultiCollectionItems(int id);
|
||||
|
||||
@@ -5,5 +5,8 @@ namespace ErsatzTV.Core.Interfaces.Scheduling;
|
||||
|
||||
public interface IPlayoutBuilder
|
||||
{
|
||||
public bool TrimStart { get; set; }
|
||||
public Playlist DebugPlaylist { get; set; }
|
||||
|
||||
Task<Playout> Build(Playout playout, PlayoutBuildMode mode, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,12 @@ public class BlockPlayoutBuilder(
|
||||
|
||||
// get all collection items for the playout
|
||||
Map<CollectionKey, List<MediaItem>> collectionMediaItems = await GetCollectionMediaItems(blocksToSchedule);
|
||||
if (collectionMediaItems.Values.All(v => v.Count == 0))
|
||||
{
|
||||
logger.LogWarning("There are no media items to schedule");
|
||||
return playout;
|
||||
}
|
||||
|
||||
Map<CollectionKey, string> collectionEtags = GetCollectionEtags(collectionMediaItems);
|
||||
|
||||
Dictionary<PlayoutItem, BlockKey> itemBlockKeys =
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace ErsatzTV.Core.Scheduling;
|
||||
public class PlaylistEnumerator : IMediaCollectionEnumerator
|
||||
{
|
||||
private IList<IMediaCollectionEnumerator> _sortedEnumerators;
|
||||
private IList<bool> _playAll;
|
||||
private int _enumeratorIndex;
|
||||
private System.Collections.Generic.HashSet<int> _idsToIncludeInEPG;
|
||||
|
||||
@@ -20,6 +21,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
|
||||
var result = new PlaylistEnumerator
|
||||
{
|
||||
_sortedEnumerators = [],
|
||||
_playAll = [],
|
||||
_idsToIncludeInEPG = [],
|
||||
Count = LCM(playlistItemMap.Values.Map(v => v.Count)) * playlistItemMap.Count
|
||||
};
|
||||
@@ -42,6 +44,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
|
||||
if (enumeratorMap.TryGetValue(collectionKey, out IMediaCollectionEnumerator enumerator))
|
||||
{
|
||||
result._sortedEnumerators.Add(enumerator);
|
||||
result._playAll.Add(playlistItem.PlayAll);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -92,6 +95,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
|
||||
{
|
||||
enumeratorMap.Add(collectionKey, enumerator);
|
||||
result._sortedEnumerators.Add(enumerator);
|
||||
result._playAll.Add(playlistItem.PlayAll);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,10 +107,15 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
|
||||
|
||||
result.State = new CollectionEnumeratorState { Seed = state.Seed };
|
||||
result._enumeratorIndex = 0;
|
||||
|
||||
// TODO: how do we end up with index > count?
|
||||
if (state.Index < result.Count)
|
||||
{
|
||||
while (result.State.Index < state.Index)
|
||||
{
|
||||
result.MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -142,7 +151,13 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
|
||||
public void MoveNext()
|
||||
{
|
||||
_sortedEnumerators[_enumeratorIndex].MoveNext();
|
||||
|
||||
// if we aren't playing all, or if we just finished playing all, move to the next enumerator
|
||||
if (!_playAll[_enumeratorIndex] || _sortedEnumerators[_enumeratorIndex].State.Index == 0)
|
||||
{
|
||||
_enumeratorIndex = (_enumeratorIndex + 1) % _sortedEnumerators.Count;
|
||||
}
|
||||
|
||||
State.Index = (State.Index + 1) % Count;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Interfaces.Scheduling;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Newtonsoft.Json;
|
||||
using Map = LanguageExt.Map;
|
||||
|
||||
@@ -18,10 +19,11 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
private readonly IArtistRepository _artistRepository;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ILogger<PlayoutBuilder> _logger;
|
||||
private ILogger<PlayoutBuilder> _logger;
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly IMultiEpisodeShuffleCollectionEnumeratorFactory _multiEpisodeFactory;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
private Playlist _debugPlaylist;
|
||||
|
||||
public PlayoutBuilder(
|
||||
IConfigElementRepository configElementRepository,
|
||||
@@ -41,6 +43,21 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool TrimStart { get; set; } = true;
|
||||
|
||||
public Playlist DebugPlaylist
|
||||
{
|
||||
get => _debugPlaylist;
|
||||
set
|
||||
{
|
||||
if (value is not null)
|
||||
{
|
||||
_debugPlaylist = value;
|
||||
_logger = NullLogger<PlayoutBuilder>.Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Playout> Build(Playout playout, PlayoutBuildMode mode, CancellationToken cancellationToken)
|
||||
{
|
||||
if (playout.ProgramSchedulePlayoutType is not ProgramSchedulePlayoutType.Flood)
|
||||
@@ -368,8 +385,11 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
if (TrimStart)
|
||||
{
|
||||
// remove old items
|
||||
playout.Items.RemoveAll(old => old.FinishOffset < trimBefore);
|
||||
}
|
||||
|
||||
// check for future items that aren't grouped inside range
|
||||
var futureItems = playout.Items.Filter(i => i.StartOffset > trimAfter).ToList();
|
||||
@@ -944,8 +964,9 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
{
|
||||
foreach (int playlistId in Optional(collectionKey.PlaylistId))
|
||||
{
|
||||
Dictionary<PlaylistItem, List<MediaItem>> playlistItemMap =
|
||||
await _mediaCollectionRepository.GetPlaylistItemMap(playlistId);
|
||||
Dictionary<PlaylistItem, List<MediaItem>> playlistItemMap = DebugPlaylist is not null
|
||||
? await _mediaCollectionRepository.GetPlaylistItemMap(DebugPlaylist)
|
||||
: await _mediaCollectionRepository.GetPlaylistItemMap(playlistId);
|
||||
|
||||
return await PlaylistEnumerator.Create(
|
||||
_mediaCollectionRepository,
|
||||
|
||||
Generated
+5774
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Add_PlaylistItemPlayAll : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "PlayAll",
|
||||
table: "PlaylistItem",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PlayAll",
|
||||
table: "PlaylistItem");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1649,6 +1649,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
|
||||
b.Property<int?>("MultiCollectionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("PlayAll")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("PlaybackOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
||||
Generated
+5613
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Add_PlaylistItemPlayAll : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "PlayAll",
|
||||
table: "PlaylistItem",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PlayAll",
|
||||
table: "PlaylistItem");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1564,6 +1564,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
||||
b.Property<int?>("MultiCollectionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("PlayAll")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PlaybackOrder")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
||||
@@ -156,6 +156,130 @@ public class MediaCollectionRepository : IMediaCollectionRepository
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Dictionary<PlaylistItem, List<MediaItem>>> GetPlaylistItemMap(Playlist playlist)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
var result = new Dictionary<PlaylistItem, List<MediaItem>>();
|
||||
|
||||
foreach (PlaylistItem playlistItem in playlist.Items)
|
||||
{
|
||||
var mediaItems = new List<MediaItem>();
|
||||
|
||||
switch (playlistItem.CollectionType)
|
||||
{
|
||||
case ProgramScheduleItemCollectionType.Collection:
|
||||
foreach (int collectionId in Optional(playlistItem.CollectionId))
|
||||
{
|
||||
|
||||
mediaItems.AddRange(await GetMovieItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetShowItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetSeasonItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetEpisodeItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetArtistItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetMusicVideoItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetOtherVideoItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetSongItems(dbContext, collectionId));
|
||||
mediaItems.AddRange(await GetImageItems(dbContext, collectionId));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.TelevisionShow:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetShowItemsFromShowId(dbContext, mediaItemId));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.TelevisionSeason:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetSeasonItemsFromSeasonId(dbContext, mediaItemId));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.Artist:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetArtistItemsFromArtistId(dbContext, mediaItemId));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.MultiCollection:
|
||||
foreach (int multiCollectionId in Optional(playlistItem.MultiCollectionId))
|
||||
{
|
||||
mediaItems.AddRange(await GetMultiCollectionItems(multiCollectionId));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.SmartCollection:
|
||||
foreach (int smartCollectionId in Optional(playlistItem.SmartCollectionId))
|
||||
{
|
||||
mediaItems.AddRange(await GetSmartCollectionItems(smartCollectionId));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.Movie:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetMovieItems(dbContext, [mediaItemId]));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.Episode:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetEpisodeItems(dbContext, [mediaItemId]));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.MusicVideo:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetMusicVideoItems(dbContext, [mediaItemId]));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.OtherVideo:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetOtherVideoItems(dbContext, [mediaItemId]));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.Song:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetSongItems(dbContext, [mediaItemId]));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ProgramScheduleItemCollectionType.Image:
|
||||
foreach (int mediaItemId in Optional(playlistItem.MediaItemId))
|
||||
{
|
||||
mediaItems.AddRange(await GetImageItems(dbContext, [mediaItemId]));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
result.Add(playlistItem, mediaItems);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Option<Collection>> GetCollectionWithCollectionItemsUntracked(int id)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<col/>
|
||||
<col/>
|
||||
<col/>
|
||||
<col/>
|
||||
<col style="width: 60px;"/>
|
||||
<col style="width: 60px;"/>
|
||||
<col style="width: 60px;"/>
|
||||
@@ -44,6 +45,7 @@
|
||||
<MudTh>Item Type</MudTh>
|
||||
<MudTh>Item</MudTh>
|
||||
<MudTh>Playback Order</MudTh>
|
||||
<MudTh>Play All</MudTh>
|
||||
<MudTh>Show In EPG</MudTh>
|
||||
<MudTh/>
|
||||
<MudTh/>
|
||||
@@ -66,6 +68,9 @@
|
||||
@(context.PlaybackOrder > 0 ? context.PlaybackOrder : "")
|
||||
</MudText>
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
<MudCheckBox T="bool" Value="@context.PlayAll" ValueChanged="@(e => UpdatePlayAll(context, e))"/>
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
<MudCheckBox T="bool" Value="@context.IncludeInProgramGuide" ValueChanged="@(e => UpdateEPG(context, e))"/>
|
||||
</MudTd>
|
||||
@@ -405,6 +410,7 @@
|
||||
SmartCollection = item.SmartCollection,
|
||||
MediaItem = item.MediaItem,
|
||||
PlaybackOrder = item.PlaybackOrder,
|
||||
PlayAll = item.PlayAll,
|
||||
IncludeInProgramGuide = item.IncludeInProgramGuide
|
||||
};
|
||||
|
||||
@@ -432,6 +438,7 @@
|
||||
MultiCollection = item.MultiCollection,
|
||||
SmartCollection = item.SmartCollection,
|
||||
MediaItem = item.MediaItem,
|
||||
PlayAll = item.PlayAll,
|
||||
IncludeInProgramGuide = item.IncludeInProgramGuide
|
||||
};
|
||||
|
||||
@@ -490,6 +497,7 @@
|
||||
item.SmartCollection?.Id,
|
||||
item.MediaItem?.MediaItemId,
|
||||
item.PlaybackOrder,
|
||||
item.PlayAll,
|
||||
item.IncludeInProgramGuide)).ToList();
|
||||
|
||||
return new ReplacePlaylistItems(Id, _playlist.Name, items);
|
||||
@@ -503,4 +511,5 @@
|
||||
|
||||
private static void UpdateEPG(PlaylistItemEditViewModel context, bool includeInProgramGuide) => context.IncludeInProgramGuide = includeInProgramGuide;
|
||||
|
||||
private static void UpdatePlayAll(PlaylistItemEditViewModel context, bool playAll) => context.PlayAll = playAll;
|
||||
}
|
||||
@@ -81,6 +81,8 @@ public class PlaylistItemEditViewModel : INotifyPropertyChanged
|
||||
|
||||
public PlaybackOrder PlaybackOrder { get; set; }
|
||||
|
||||
public bool PlayAll { get; set; }
|
||||
|
||||
public bool IncludeInProgramGuide { get; set; }
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
Reference in New Issue
Block a user