using Jellyfin.Plugin.MediaCleaner.Helpers; using Jellyfin.Plugin.MediaCleaner.Models; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Web; using Microsoft.AspNetCore.Http; using System.Linq; namespace Jellyfin.Plugin.MediaCleaner.Controllers; public record ConnectionTestRequest(string Address, string ApiKey); public record RadarrMovie( [property: JsonPropertyName("id")] int? Id, [property: JsonPropertyName("title")] string? Title ); [Route("radarr")] public class RadarrController : Controller { private static Configuration Configuration => Plugin.Instance!.Configuration; private readonly HttpClient _httpClient; public RadarrController(HttpClient httpClient) { _httpClient = httpClient; // Set the default request headers _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _httpClient.DefaultRequestHeaders.Add("X-Api-Key", Configuration.RadarrAPIKey); } private async Task GetRadarrMovieInfo(MovieInfo movieInfo) { var responseBody = await HttpHelper.SendHttpRequestAsync( _httpClient, Configuration.RadarrAddress, HttpMethod.Get, $"/api/v3/movie?tmdbId={Uri.EscapeDataString(movieInfo.TmdbId ?? string.Empty)}&excludeLocalCovers=false" ).ConfigureAwait(false); var movies = JsonSerializer.Deserialize>(responseBody.GetRawText()); var movie = movies?.FirstOrDefault(); if (movie == null) { return NotFound("Movie not found in Radarr library."); } return Ok(movie); } [HttpPost("deleteMovieFromRadarr")] public async Task DeleteMovieFromRadarr([FromBody] MovieInfo movieInfo){ if (movieInfo == null || string.IsNullOrEmpty(movieInfo.TmdbId)) { return BadRequest("Invalid movie information provided."); } try { var radarrMovieInfoResult = await GetRadarrMovieInfo(movieInfo).ConfigureAwait(false); if(radarrMovieInfoResult.StatusCode != StatusCodes.Status200OK || radarrMovieInfoResult.Value is not RadarrMovie){ return radarrMovieInfoResult; } RadarrMovie movie = (RadarrMovie)radarrMovieInfoResult.Value; var responseBody = await HttpHelper.SendHttpRequestAsync( _httpClient, Configuration.RadarrAddress, HttpMethod.Delete, $"/api/v3/movie/{movie.Id}?deleteFiles=true&addImportExclusion=true" ).ConfigureAwait(false); // Radarr typically returns an empty body on successful delete. return Ok(responseBody); } catch (HttpRequestException e) { return StatusCode(StatusCodes.Status500InternalServerError, $"An unexpected error occurred. {e.Message}"); } } [HttpPost("testConnection")] public async Task TestConnection([FromBody] ConnectionTestRequest request) { if (request == null || string.IsNullOrWhiteSpace(request.Address) || string.IsNullOrWhiteSpace(request.ApiKey)) { return BadRequest("Address and ApiKey are required."); } var address = request.Address.Trim(); if (!address.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !address.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { address = "http://" + address; } try { using var httpRequest = new HttpRequestMessage(HttpMethod.Get, address); httpRequest.Headers.Add("X-Api-Key", request.ApiKey); var response = await _httpClient.SendAsync(httpRequest).ConfigureAwait(false); return Ok(new { success = response.IsSuccessStatusCode }); } catch (HttpRequestException e) { return StatusCode(StatusCodes.Status502BadGateway, e.Message); } catch (Exception e) { return StatusCode(StatusCodes.Status500InternalServerError, e.Message); } } }