mirror of
https://github.com/Sonarr/Sonarr
synced 2025-12-06 08:28:37 +01:00
Add v5 episode file endpoints
This commit is contained in:
parent
d2107e92f3
commit
9cdf1bf721
4 changed files with 217 additions and 4 deletions
201
src/Sonarr.Api.V5/EpisodeFiles/EpisodeFileController.cs
Normal file
201
src/Sonarr.Api.V5/EpisodeFiles/EpisodeFileController.cs
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
using System.Net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.SignalR;
|
||||
using Sonarr.Http;
|
||||
using Sonarr.Http.REST;
|
||||
using Sonarr.Http.REST.Attributes;
|
||||
using BadRequestException = Sonarr.Http.REST.BadRequestException;
|
||||
|
||||
namespace Sonarr.Api.V5.EpisodeFiles;
|
||||
|
||||
[V5ApiController]
|
||||
public class EpisodeFileController : RestControllerWithSignalR<EpisodeFileResource, EpisodeFile>,
|
||||
IHandle<EpisodeFileAddedEvent>,
|
||||
IHandle<EpisodeFileDeletedEvent>
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IDeleteMediaFiles _mediaFileDeletionService;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly IUpgradableSpecification _upgradableSpecification;
|
||||
|
||||
public EpisodeFileController(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMediaFileService mediaFileService,
|
||||
IDeleteMediaFiles mediaFileDeletionService,
|
||||
ISeriesService seriesService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IUpgradableSpecification upgradableSpecification)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_mediaFileService = mediaFileService;
|
||||
_mediaFileDeletionService = mediaFileDeletionService;
|
||||
_seriesService = seriesService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_upgradableSpecification = upgradableSpecification;
|
||||
}
|
||||
|
||||
protected override EpisodeFileResource GetResourceById(int id)
|
||||
{
|
||||
var episodeFile = _mediaFileService.Get(id);
|
||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
||||
|
||||
var resource = episodeFile.ToResource(series, _upgradableSpecification, _formatCalculator);
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Produces("application/json")]
|
||||
public List<EpisodeFileResource> GetEpisodeFiles(int? seriesId, [FromQuery] List<int>? episodeFileIds)
|
||||
{
|
||||
if (!seriesId.HasValue && episodeFileIds?.Any() == false)
|
||||
{
|
||||
throw new BadRequestException("seriesId or episodeFileIds must be provided");
|
||||
}
|
||||
|
||||
if (seriesId.HasValue)
|
||||
{
|
||||
var series = _seriesService.GetSeries(seriesId.Value);
|
||||
var files = _mediaFileService.GetFilesBySeries(seriesId.Value);
|
||||
|
||||
if (files == null)
|
||||
{
|
||||
return new List<EpisodeFileResource>();
|
||||
}
|
||||
|
||||
return files.ConvertAll(e => e.ToResource(series, _upgradableSpecification, _formatCalculator));
|
||||
}
|
||||
else
|
||||
{
|
||||
var episodeFiles = _mediaFileService.Get(episodeFileIds);
|
||||
|
||||
return episodeFiles.GroupBy(e => e.SeriesId)
|
||||
.SelectMany(f => f.ToList()
|
||||
.ConvertAll(e => e.ToResource(_seriesService.GetSeries(f.Key), _upgradableSpecification, _formatCalculator)))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
[RestPutById]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<EpisodeFileResource> SetQuality([FromBody] EpisodeFileResource episodeFileResource)
|
||||
{
|
||||
var episodeFile = _mediaFileService.Get(episodeFileResource.Id);
|
||||
episodeFile.Quality = episodeFileResource.Quality;
|
||||
|
||||
if (episodeFileResource.SceneName != null && SceneChecker.IsSceneTitle(episodeFileResource.SceneName))
|
||||
{
|
||||
episodeFile.SceneName = episodeFileResource.SceneName;
|
||||
}
|
||||
|
||||
if (episodeFileResource.ReleaseGroup != null)
|
||||
{
|
||||
episodeFile.ReleaseGroup = episodeFileResource.ReleaseGroup;
|
||||
}
|
||||
|
||||
_mediaFileService.Update(episodeFile);
|
||||
return Accepted(episodeFile.Id);
|
||||
}
|
||||
|
||||
[RestDeleteById]
|
||||
public void DeleteEpisodeFile(int id)
|
||||
{
|
||||
var episodeFile = _mediaFileService.Get(id);
|
||||
|
||||
if (episodeFile == null)
|
||||
{
|
||||
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Episode file not found");
|
||||
}
|
||||
|
||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
||||
|
||||
_mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile);
|
||||
}
|
||||
|
||||
[HttpDelete("bulk")]
|
||||
[Consumes("application/json")]
|
||||
public object DeleteEpisodeFiles([FromBody] EpisodeFileListResource resource)
|
||||
{
|
||||
var episodeFiles = _mediaFileService.GetFiles(resource.EpisodeFileIds);
|
||||
var series = _seriesService.GetSeries(episodeFiles.First().SeriesId);
|
||||
|
||||
foreach (var episodeFile in episodeFiles)
|
||||
{
|
||||
_mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile);
|
||||
}
|
||||
|
||||
return new { };
|
||||
}
|
||||
|
||||
[HttpPut("bulk")]
|
||||
[Consumes("application/json")]
|
||||
public object SetPropertiesBulk([FromBody] List<EpisodeFileResource> resources)
|
||||
{
|
||||
var episodeFiles = _mediaFileService.GetFiles(resources.Select(r => r.Id));
|
||||
|
||||
foreach (var episodeFile in episodeFiles)
|
||||
{
|
||||
var resourceEpisodeFile = resources.Single(r => r.Id == episodeFile.Id);
|
||||
|
||||
if (resourceEpisodeFile.Languages != null)
|
||||
{
|
||||
// Don't allow user to set files with 'Original' language
|
||||
episodeFile.Languages = resourceEpisodeFile.Languages.Where(l => l != null && l != Language.Original).ToList();
|
||||
}
|
||||
|
||||
if (resourceEpisodeFile.Quality != null)
|
||||
{
|
||||
episodeFile.Quality = resourceEpisodeFile.Quality;
|
||||
}
|
||||
|
||||
if (resourceEpisodeFile.SceneName != null && SceneChecker.IsSceneTitle(resourceEpisodeFile.SceneName))
|
||||
{
|
||||
episodeFile.SceneName = resourceEpisodeFile.SceneName;
|
||||
}
|
||||
|
||||
if (resourceEpisodeFile.ReleaseGroup != null)
|
||||
{
|
||||
episodeFile.ReleaseGroup = resourceEpisodeFile.ReleaseGroup;
|
||||
}
|
||||
|
||||
if (resourceEpisodeFile.IndexerFlags.HasValue)
|
||||
{
|
||||
episodeFile.IndexerFlags = (IndexerFlags)resourceEpisodeFile.IndexerFlags;
|
||||
}
|
||||
|
||||
if (resourceEpisodeFile.ReleaseType != null)
|
||||
{
|
||||
episodeFile.ReleaseType = (ReleaseType)resourceEpisodeFile.ReleaseType;
|
||||
}
|
||||
}
|
||||
|
||||
_mediaFileService.Update(episodeFiles);
|
||||
|
||||
var series = _seriesService.GetSeries(episodeFiles.First().SeriesId);
|
||||
|
||||
return Accepted(episodeFiles.ConvertAll(f => f.ToResource(series, _upgradableSpecification, _formatCalculator)));
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public void Handle(EpisodeFileAddedEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
public void Handle(EpisodeFileDeletedEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Deleted, message.EpisodeFile.Id);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
namespace Sonarr.Api.V5.EpisodeFiles
|
||||
{
|
||||
public class EpisodeFileListResource
|
||||
{
|
||||
public List<int> EpisodeFileIds { get; set; } = [];
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,9 @@ public class EpisodeFileResource : RestResource
|
|||
public DateTime DateAdded { get; set; }
|
||||
public string? SceneName { get; set; }
|
||||
public string? ReleaseGroup { get; set; }
|
||||
public required List<Language> Languages { get; set; }
|
||||
public required QualityModel Quality { get; set; }
|
||||
public required List<CustomFormatResource> CustomFormats { get; set; }
|
||||
public List<Language> Languages { get; set; } = [];
|
||||
public QualityModel? Quality { get; set; }
|
||||
public List<CustomFormatResource> CustomFormats { get; set; } = [];
|
||||
public int CustomFormatScore { get; set; }
|
||||
public int? IndexerFlags { get; set; }
|
||||
public ReleaseType? ReleaseType { get; set; }
|
||||
|
|
|
|||
|
|
@ -25,8 +25,13 @@ public class MediaInfoResource : RestResource
|
|||
|
||||
public static class MediaInfoResourceMapper
|
||||
{
|
||||
public static MediaInfoResource ToResource(this MediaInfoModel model, string sceneName)
|
||||
public static MediaInfoResource? ToResource(this MediaInfoModel? model, string sceneName)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MediaInfoResource
|
||||
{
|
||||
AudioBitrate = model.AudioBitrate,
|
||||
|
|
|
|||
Loading…
Reference in a new issue