Browse Source

add reset playout and scan library api endpoints (#1833)

* add api to reset playout

* add library scan api

* update changelog
pull/1834/head
Jason Dove 10 months ago committed by GitHub
parent
commit
5e0fb31069
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      CHANGELOG.md
  2. 3
      ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryId.cs
  3. 73
      ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryIdHandler.cs
  4. 3
      ErsatzTV.Application/Playouts/Queries/GetPlayoutIdByChannelNumber.cs
  5. 18
      ErsatzTV.Application/Playouts/Queries/GetPlayoutIdByChannelNumberHandler.cs
  6. 27
      ErsatzTV/Controllers/Api/ChannelController.cs
  7. 17
      ErsatzTV/Controllers/Api/LibrariesController.cs

5
CHANGELOG.md

@ -32,6 +32,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -32,6 +32,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `repeat`: continue building the playout from the first instruction in the YAML file
- Add channel logo generation by @raknam
- Channels without custom uploaded logos will automatically generate a logo that includes the channel name
- Add two new API endpoints
- Reset playout for channel
- POST `/api/channels/{channelNumber}/playout/reset`
- Scan library
- POST `/api/libraries/{libraryId}/scan`
### Fixed
- Add basic cache busting to XMLTV image URLs

3
ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryId.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Libraries;
public record QueueLibraryScanByLibraryId(int LibraryId) : IRequest<bool>;

73
ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryIdHandler.cs

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
using System.Threading.Channels;
using ErsatzTV.Application.Emby;
using ErsatzTV.Application.Jellyfin;
using ErsatzTV.Application.MediaSources;
using ErsatzTV.Application.Plex;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Locking;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.Application.Libraries;
public class QueueLibraryScanByLibraryIdHandler(
IDbContextFactory<TvContext> dbContextFactory,
IEntityLocker locker,
ChannelWriter<IScannerBackgroundServiceRequest> scannerWorker,
ILogger<QueueLibraryScanByLibraryIdHandler> logger)
: IRequestHandler<QueueLibraryScanByLibraryId, bool>
{
public async Task<bool> Handle(QueueLibraryScanByLibraryId request, CancellationToken cancellationToken)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
Option<Library> maybeLibrary = await dbContext.Libraries
.AsNoTracking()
.SelectOneAsync(l => l.Id, l => l.Id == request.LibraryId);
foreach (Library library in maybeLibrary)
{
if (locker.LockLibrary(library.Id))
{
logger.LogDebug("Queued library scan for library id {Id}", library.Id);
switch (library)
{
case LocalLibrary:
await scannerWorker.WriteAsync(new ForceScanLocalLibrary(library.Id), cancellationToken);
break;
case PlexLibrary:
await scannerWorker.WriteAsync(
new SynchronizePlexLibraries(library.MediaSourceId),
cancellationToken);
await scannerWorker.WriteAsync(
new ForceSynchronizePlexLibraryById(library.Id, false),
cancellationToken);
break;
case JellyfinLibrary:
await scannerWorker.WriteAsync(
new SynchronizeJellyfinLibraries(library.MediaSourceId),
cancellationToken);
await scannerWorker.WriteAsync(
new ForceSynchronizeJellyfinLibraryById(library.Id, false),
cancellationToken);
break;
case EmbyLibrary:
await scannerWorker.WriteAsync(
new SynchronizeEmbyLibraries(library.MediaSourceId),
cancellationToken);
await scannerWorker.WriteAsync(
new ForceSynchronizeEmbyLibraryById(library.Id, false),
cancellationToken);
break;
}
}
return true;
}
return false;
}
}

3
ErsatzTV.Application/Playouts/Queries/GetPlayoutIdByChannelNumber.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Playouts;
public record GetPlayoutIdByChannelNumber(string ChannelNumber) : IRequest<Option<int>>;

18
ErsatzTV.Application/Playouts/Queries/GetPlayoutIdByChannelNumberHandler.cs

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Playouts;
public class GetPlayoutIdByChannelNumberHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetPlayoutIdByChannelNumber, Option<int>>
{
public async Task<Option<int>> Handle(GetPlayoutIdByChannelNumber request, CancellationToken cancellationToken)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
return await dbContext.Playouts
.Filter(p => p.Channel.Number == request.ChannelNumber)
.Map(p => p.Id)
.ToListAsync(cancellationToken)
.Map(list => list.HeadOrNone());
}
}

27
ErsatzTV/Controllers/Api/ChannelController.cs

@ -1,5 +1,9 @@ @@ -1,5 +1,9 @@
using ErsatzTV.Application.Channels;
using System.Threading.Channels;
using ErsatzTV.Application;
using ErsatzTV.Application.Channels;
using ErsatzTV.Application.Playouts;
using ErsatzTV.Core.Api.Channels;
using ErsatzTV.Core.Scheduling;
using ErsatzTV.Filters;
using MediatR;
using Microsoft.AspNetCore.Mvc;
@ -7,13 +11,22 @@ using Microsoft.AspNetCore.Mvc; @@ -7,13 +11,22 @@ using Microsoft.AspNetCore.Mvc;
namespace ErsatzTV.Controllers.Api;
[ApiController]
[V2ApiActionFilter]
public class ChannelController
public class ChannelController(ChannelWriter<IBackgroundServiceRequest> workerChannel, IMediator mediator)
{
private readonly IMediator _mediator;
[HttpGet("/api/channels")]
[V2ApiActionFilter]
public async Task<List<ChannelResponseModel>> GetAll() => await mediator.Send(new GetAllChannelsForApi());
public ChannelController(IMediator mediator) => _mediator = mediator;
[HttpPost("/api/channels/{channelNumber}/playout/reset")]
public async Task<IActionResult> ResetPlayout(string channelNumber)
{
Option<int> maybePlayoutId = await mediator.Send(new GetPlayoutIdByChannelNumber(channelNumber));
foreach (int playoutId in maybePlayoutId)
{
await workerChannel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Reset));
return new OkResult();
}
[HttpGet("/api/channels")]
public async Task<List<ChannelResponseModel>> GetAll() => await _mediator.Send(new GetAllChannelsForApi());
return new NotFoundResult();
}
}

17
ErsatzTV/Controllers/Api/LibrariesController.cs

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
using ErsatzTV.Application.Libraries;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace ErsatzTV.Controllers.Api;
[ApiController]
public class LibrariesController(IMediator mediator)
{
[HttpPost("/api/libraries/{id:int}/scan")]
public async Task<IActionResult> ResetPlayout(int id)
{
return await mediator.Send(new QueueLibraryScanByLibraryId(id))
? new OkResult()
: new NotFoundResult();
}
}
Loading…
Cancel
Save