feat(api): composite create-channel endpoint (#63)
This commit is contained in:
@@ -79,6 +79,25 @@ public class ChannelController(ChannelWriter<IBackgroundServiceRequest> workerCh
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("/api/channels/from-lineup", Name = "CreateChannelFromLineup")]
|
||||
[Tags("Channels")]
|
||||
[EndpointSummary("Create a channel from a library lineup")]
|
||||
[EndpointDescription(
|
||||
"Atomically creates the channel, generated lineup collection, program schedule, schedule items, " +
|
||||
"and classic playout. Template defaults are stamped at create time; advanced overrides win.")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(CreateChannelFromLineupResponseModel), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
|
||||
public async Task<IActionResult> CreateFromLineup(
|
||||
[Required] [FromBody] CreateChannelFromLineupRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Either<BaseError, CreateChannelFromLineupResponseModel> result =
|
||||
await mediator.Send(request.ToCommand(), cancellationToken);
|
||||
return result.ToCreatedResult(response => $"/api/channels/{response.ChannelId}", response => response);
|
||||
}
|
||||
|
||||
[HttpPut("/api/channels/{id:int}")]
|
||||
[Tags("Channels")]
|
||||
[EndpointSummary("Update a channel")]
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#nullable enable
|
||||
|
||||
using ErsatzTV.Application.Artworks;
|
||||
using ErsatzTV.Application.Channels;
|
||||
using ErsatzTV.Core.Api.LibraryBrowse;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Scheduling;
|
||||
|
||||
namespace ErsatzTV.Controllers.Api.Requests;
|
||||
|
||||
public record CreateChannelFromLineupRequest(
|
||||
string Name,
|
||||
string Number,
|
||||
string Group,
|
||||
string Categories,
|
||||
ArtworkContentTypeModel Logo,
|
||||
bool IsEnabled,
|
||||
bool ShowInEpg,
|
||||
int TemplateId,
|
||||
CreateChannelFromLineupAdvancedOptionsRequest? Advanced,
|
||||
List<CreateChannelFromLineupItemRequest> Lineup)
|
||||
{
|
||||
public CreateChannelFromLineup ToCommand() =>
|
||||
new(
|
||||
Name,
|
||||
Number,
|
||||
Group,
|
||||
Categories,
|
||||
Logo,
|
||||
IsEnabled,
|
||||
ShowInEpg,
|
||||
TemplateId,
|
||||
Advanced?.ToCommand() ?? new CreateChannelFromLineupAdvancedOptions(),
|
||||
Lineup.Map(i => i.ToCommand()).ToList());
|
||||
}
|
||||
|
||||
public record CreateChannelFromLineupAdvancedOptionsRequest(
|
||||
PlaybackOrder? PlaybackOrder = null,
|
||||
int? FFmpegProfileId = null,
|
||||
int? WatermarkId = null,
|
||||
int? FallbackFillerId = null,
|
||||
int? PreRollFillerId = null,
|
||||
int? MidRollFillerId = null,
|
||||
int? PostRollFillerId = null,
|
||||
ChannelStreamSelectorMode? StreamSelectorMode = null,
|
||||
string? StreamSelector = null,
|
||||
string? PreferredAudioLanguageCode = null,
|
||||
string? PreferredAudioTitle = null,
|
||||
ChannelPlayoutSource? PlayoutSource = null,
|
||||
ChannelPlayoutMode? PlayoutMode = null,
|
||||
StreamingMode? StreamingMode = null,
|
||||
string? PreferredSubtitleLanguageCode = null,
|
||||
ChannelSubtitleMode? SubtitleMode = null,
|
||||
ChannelMusicVideoCreditsMode? MusicVideoCreditsMode = null,
|
||||
string? MusicVideoCreditsTemplate = null,
|
||||
ChannelSongVideoMode? SongVideoMode = null,
|
||||
ChannelTranscodeMode? TranscodeMode = null,
|
||||
ChannelIdleBehavior? IdleBehavior = null,
|
||||
bool? ShuffleScheduleItems = null,
|
||||
bool? RandomStartPoint = null,
|
||||
FixedStartTimeBehavior? FixedStartTimeBehavior = null)
|
||||
{
|
||||
public CreateChannelFromLineupAdvancedOptions ToCommand() =>
|
||||
new(
|
||||
PlaybackOrder,
|
||||
FFmpegProfileId,
|
||||
WatermarkId,
|
||||
FallbackFillerId,
|
||||
PreRollFillerId,
|
||||
MidRollFillerId,
|
||||
PostRollFillerId,
|
||||
StreamSelectorMode,
|
||||
StreamSelector,
|
||||
PreferredAudioLanguageCode,
|
||||
PreferredAudioTitle,
|
||||
PlayoutSource,
|
||||
PlayoutMode,
|
||||
StreamingMode,
|
||||
PreferredSubtitleLanguageCode,
|
||||
SubtitleMode,
|
||||
MusicVideoCreditsMode,
|
||||
MusicVideoCreditsTemplate,
|
||||
SongVideoMode,
|
||||
TranscodeMode,
|
||||
IdleBehavior,
|
||||
ShuffleScheduleItems,
|
||||
RandomStartPoint,
|
||||
FixedStartTimeBehavior);
|
||||
}
|
||||
|
||||
public record CreateChannelFromLineupItemRequest(
|
||||
LibraryBrowseMediaType MediaType,
|
||||
CollectionType CollectionType,
|
||||
int? CollectionId,
|
||||
int? MultiCollectionId,
|
||||
int? SmartCollectionId,
|
||||
int? RerunCollectionId,
|
||||
int? MediaItemId,
|
||||
int? PlaylistId)
|
||||
{
|
||||
public CreateChannelFromLineupItem ToCommand() =>
|
||||
new(
|
||||
MediaType,
|
||||
CollectionType,
|
||||
CollectionId,
|
||||
MultiCollectionId,
|
||||
SmartCollectionId,
|
||||
RerunCollectionId,
|
||||
MediaItemId,
|
||||
PlaylistId);
|
||||
}
|
||||
@@ -531,6 +531,103 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/channels/from-lineup": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Channels"
|
||||
],
|
||||
"summary": "Create a channel from a library lineup",
|
||||
"description": "Atomically creates the channel, generated lineup collection, program schedule, schedule items, and classic playout. Template defaults are stamped at create time; advanced overrides win.",
|
||||
"operationId": "CreateChannelFromLineup",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json-patch+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupRequest"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupRequest"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupRequest"
|
||||
}
|
||||
},
|
||||
"application/*+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupResponseModel"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupResponseModel"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupResponseModel"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Unprocessable Entity",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
},
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/channels/bulk/renumber": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -5187,6 +5284,353 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateChannelFromLineupAdvancedOptionsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"playbackOrder": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PlaybackOrder"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fFmpegProfileId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"watermarkId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"fallbackFillerId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"preRollFillerId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"midRollFillerId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"postRollFillerId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"streamSelectorMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelStreamSelectorMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSelector": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"preferredAudioLanguageCode": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"preferredAudioTitle": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"playoutSource": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelPlayoutSource"
|
||||
}
|
||||
]
|
||||
},
|
||||
"playoutMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelPlayoutMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamingMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/StreamingMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"preferredSubtitleLanguageCode": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"subtitleMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelSubtitleMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"musicVideoCreditsMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelMusicVideoCreditsMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"musicVideoCreditsTemplate": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"songVideoMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelSongVideoMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"transcodeMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelTranscodeMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"idleBehavior": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ChannelIdleBehavior"
|
||||
}
|
||||
]
|
||||
},
|
||||
"shuffleScheduleItems": {
|
||||
"type": [
|
||||
"null",
|
||||
"boolean"
|
||||
]
|
||||
},
|
||||
"randomStartPoint": {
|
||||
"type": [
|
||||
"null",
|
||||
"boolean"
|
||||
]
|
||||
},
|
||||
"fixedStartTimeBehavior": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/FixedStartTimeBehavior"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateChannelFromLineupItemRequest": {
|
||||
"required": [
|
||||
"mediaType",
|
||||
"collectionType",
|
||||
"collectionId",
|
||||
"multiCollectionId",
|
||||
"smartCollectionId",
|
||||
"rerunCollectionId",
|
||||
"mediaItemId",
|
||||
"playlistId"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mediaType": {
|
||||
"$ref": "#/components/schemas/LibraryBrowseMediaType"
|
||||
},
|
||||
"collectionType": {
|
||||
"$ref": "#/components/schemas/CollectionType"
|
||||
},
|
||||
"collectionId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"multiCollectionId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"smartCollectionId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"rerunCollectionId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"mediaItemId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"playlistId": {
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateChannelFromLineupRequest": {
|
||||
"required": [
|
||||
"name",
|
||||
"number",
|
||||
"group",
|
||||
"categories",
|
||||
"logo",
|
||||
"isEnabled",
|
||||
"showInEpg",
|
||||
"templateId",
|
||||
"advanced",
|
||||
"lineup"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"number": {
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"categories": {
|
||||
"type": "string"
|
||||
},
|
||||
"logo": {
|
||||
"$ref": "#/components/schemas/ArtworkContentTypeModel"
|
||||
},
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"showInEpg": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"templateId": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"advanced": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupAdvancedOptionsRequest"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lineup": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/CreateChannelFromLineupItemRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateChannelFromLineupResponseModel": {
|
||||
"required": [
|
||||
"channelId",
|
||||
"collectionId",
|
||||
"programScheduleId",
|
||||
"playoutId"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channelId": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"collectionId": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"programScheduleId": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"playoutId": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateChannelRequest": {
|
||||
"required": [
|
||||
"name",
|
||||
|
||||
Reference in New Issue
Block a user