From ef9bba8a52dc93bda096098c86b9370b5b75edd4 Mon Sep 17 00:00:00 2001 From: Timothy Date: Fri, 17 Jul 2026 12:40:48 +0200 Subject: [PATCH] feat(70): accept per-source weight on the multi-collection API Without this the weight is only reachable by editing the database, so the enumerator has nothing to distribute by. Weight is threaded through create and update (all four handler branches: add and update, plain and smart) and defaults to 1, so it is optional on the wire and /api/v1 stays additive under the freeze. It is returned on the read path too, which is load-bearing rather than symmetry: the update replaces the item list, so a client that GETs, edits a name, and PUTs back would silently reset every weight to the default if the GET didn't carry it. Weight edits ride the existing MultiCollection Version token, so If-Match/412 concurrency needs no new design. Regenerated v1.json + v1.d.ts + endpoint-index via update-openapi.sh and generate:api (never hand-edited). The spec picks up weight on both request and response models and WeightedShuffle in the PlaybackOrder enum; weight is emitted optional. Refs #70 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Commands/CreateMultiCollection.cs | 3 ++- .../Commands/CreateMultiCollectionHandler.cs | 6 ++++-- .../Commands/UpdateMultiCollection.cs | 3 ++- .../Commands/UpdateMultiCollectionHandler.cs | 10 +++++++--- ErsatzTV.Application/MediaCollections/Mapper.cs | 6 ++++-- .../MultiCollectionItemViewModel.cs | 3 ++- .../MultiCollectionSmartItemViewModel.cs | 3 ++- .../MultiCollectionResponseModel.cs | 3 ++- .../Controllers/Api/MultiCollectionController.cs | 6 ++++-- .../Api/Requests/CreateMultiCollectionRequest.cs | 6 ++++-- .../Api/Requests/UpdateMultiCollectionRequest.cs | 3 ++- ErsatzTV/wwwroot/openapi/v1.json | 13 ++++++++++++- web/src/api/generated/v1.d.ts | 4 +++- 13 files changed, 50 insertions(+), 19 deletions(-) diff --git a/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollection.cs index a3830c13e..e49df679b 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollection.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollection.cs @@ -7,7 +7,8 @@ public record CreateMultiCollectionItem( int? CollectionId, int? SmartCollectionId, bool ScheduleAsGroup, - PlaybackOrder PlaybackOrder); + PlaybackOrder PlaybackOrder, + int Weight = 1); public record CreateMultiCollection(string Name, List Items) : IRequest>; diff --git a/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollectionHandler.cs index 9410ca8c2..cf0bd7ded 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/CreateMultiCollectionHandler.cs @@ -63,7 +63,8 @@ public class CreateMultiCollectionHandler : { CollectionId = i.CollectionId.Value, ScheduleAsGroup = i.ScheduleAsGroup, - PlaybackOrder = i.PlaybackOrder + PlaybackOrder = i.PlaybackOrder, + Weight = i.Weight }); } @@ -79,7 +80,8 @@ public class CreateMultiCollectionHandler : { SmartCollectionId = i.SmartCollectionId.Value, ScheduleAsGroup = i.ScheduleAsGroup, - PlaybackOrder = i.PlaybackOrder + PlaybackOrder = i.PlaybackOrder, + Weight = i.Weight }); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollection.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollection.cs index 911347c6f..9571ee345 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollection.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollection.cs @@ -7,7 +7,8 @@ public record UpdateMultiCollectionItem( int? CollectionId, int? SmartCollectionId, bool ScheduleAsGroup, - PlaybackOrder PlaybackOrder); + PlaybackOrder PlaybackOrder, + int Weight = 1); public record UpdateMultiCollection( int MultiCollectionId, diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs index 0d4abcb53..dde2c7fd1 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs @@ -1,4 +1,4 @@ -using System.Threading.Channels; +using System.Threading.Channels; using ErsatzTV.Application.Playouts; using ErsatzTV.Core; using ErsatzTV.Core.Domain; @@ -76,7 +76,8 @@ public class UpdateMultiCollectionHandler : IRequestHandler @@ -78,7 +79,8 @@ internal static class Mapper multiCollectionSmartItem.MultiCollectionId, ProjectToViewModel(multiCollectionSmartItem.SmartCollection), multiCollectionSmartItem.ScheduleAsGroup, - multiCollectionSmartItem.PlaybackOrder); + multiCollectionSmartItem.PlaybackOrder, + multiCollectionSmartItem.Weight); internal static TreeViewModel ProjectToViewModel(List playlistGroups) => new( diff --git a/ErsatzTV.Application/MediaCollections/MultiCollectionItemViewModel.cs b/ErsatzTV.Application/MediaCollections/MultiCollectionItemViewModel.cs index 146c581da..41367cada 100644 --- a/ErsatzTV.Application/MediaCollections/MultiCollectionItemViewModel.cs +++ b/ErsatzTV.Application/MediaCollections/MultiCollectionItemViewModel.cs @@ -6,4 +6,5 @@ public record MultiCollectionItemViewModel( int MultiCollectionId, MediaCollectionViewModel Collection, bool ScheduleAsGroup, - PlaybackOrder PlaybackOrder); + PlaybackOrder PlaybackOrder, + int Weight = 1); diff --git a/ErsatzTV.Application/MediaCollections/MultiCollectionSmartItemViewModel.cs b/ErsatzTV.Application/MediaCollections/MultiCollectionSmartItemViewModel.cs index 4f733de0b..047504bc2 100644 --- a/ErsatzTV.Application/MediaCollections/MultiCollectionSmartItemViewModel.cs +++ b/ErsatzTV.Application/MediaCollections/MultiCollectionSmartItemViewModel.cs @@ -6,4 +6,5 @@ public record MultiCollectionSmartItemViewModel( int MultiCollectionId, SmartCollectionViewModel SmartCollection, bool ScheduleAsGroup, - PlaybackOrder PlaybackOrder); + PlaybackOrder PlaybackOrder, + int Weight = 1); diff --git a/ErsatzTV.Core/Api/MediaCollections/MultiCollectionResponseModel.cs b/ErsatzTV.Core/Api/MediaCollections/MultiCollectionResponseModel.cs index 4117e95cc..b4c5dbe3a 100644 --- a/ErsatzTV.Core/Api/MediaCollections/MultiCollectionResponseModel.cs +++ b/ErsatzTV.Core/Api/MediaCollections/MultiCollectionResponseModel.cs @@ -13,7 +13,8 @@ public record MultiCollectionItemResponseModel( int? SmartCollectionId, string Name, bool ScheduleAsGroup, - PlaybackOrder PlaybackOrder); + PlaybackOrder PlaybackOrder, + int Weight = 1); public record PagedMultiCollectionsResponseModel( int TotalCount, diff --git a/ErsatzTV/Controllers/Api/MultiCollectionController.cs b/ErsatzTV/Controllers/Api/MultiCollectionController.cs index 2422334c5..21796e795 100644 --- a/ErsatzTV/Controllers/Api/MultiCollectionController.cs +++ b/ErsatzTV/Controllers/Api/MultiCollectionController.cs @@ -136,13 +136,15 @@ public class MultiCollectionController(IMediator mediator) : ControllerBase null, i.Collection.Name, i.ScheduleAsGroup, - i.PlaybackOrder)) + i.PlaybackOrder, + i.Weight)) .Concat(vm.SmartItems.Map(i => new MultiCollectionItemResponseModel( null, i.SmartCollection.Id, i.SmartCollection.Name, i.ScheduleAsGroup, - i.PlaybackOrder))) + i.PlaybackOrder, + i.Weight))) .ToList(); return new MultiCollectionResponseModel(vm.Id, vm.Name, items); diff --git a/ErsatzTV/Controllers/Api/Requests/CreateMultiCollectionRequest.cs b/ErsatzTV/Controllers/Api/Requests/CreateMultiCollectionRequest.cs index 5d655fd2d..783d08fec 100644 --- a/ErsatzTV/Controllers/Api/Requests/CreateMultiCollectionRequest.cs +++ b/ErsatzTV/Controllers/Api/Requests/CreateMultiCollectionRequest.cs @@ -9,7 +9,8 @@ public record MultiCollectionItemRequest( int? CollectionId, int? SmartCollectionId, bool ScheduleAsGroup, - PlaybackOrder PlaybackOrder); + PlaybackOrder PlaybackOrder, + int Weight = 1); public record CreateMultiCollectionRequest(string Name, List Items) { @@ -21,6 +22,7 @@ public record CreateMultiCollectionRequest(string Name, List