using API.Schema.MangaContext;
using API.Schema.MangaContext.MetadataFetchers;
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.EntityFrameworkCore;
using static Microsoft.AspNetCore.Http.StatusCodes;
// ReSharper disable InconsistentNaming
namespace API.Controllers;
[ApiVersion(2)]
[ApiController]
[Route("v{v:apiVersion}/[controller]")]
public class MetadataFetcherController(MangaContext context) : Controller
{
///
/// Get all (Metadata-Sites)
///
/// Names of (Metadata-Sites)
[HttpGet]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetConnectors ()
{
return Ok(Tranga.MetadataFetchers.Select(m => m.Name).ToArray());
}
///
/// Returns all
///
///
/// Error during Database Operation
[HttpGet("Links")]
[ProducesResponseType(Status200OK, "application/json")]
public async Task GetLinkedEntries ()
{
if (await context.MetadataEntries.ToArrayAsync() is not { } result)
return StatusCode(Status500InternalServerError);
return Ok(result);
}
///
/// Searches (Metadata-Sites) for Manga-Metadata
///
/// .Key
/// .Name
/// Instead of using the for search on Website, use a specific term
///
/// (Metadata-Sites) with does not exist
/// with not found
[HttpPost("{MetadataFetcherName}/SearchManga/{MangaId}")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
public async Task SearchMangaMetadata(string MangaId, string MetadataFetcherName, [FromBody (EmptyBodyBehavior = EmptyBodyBehavior.Allow)]string? searchTerm = null)
{
if (await context.Mangas.FirstOrDefaultAsync(m => m.Key == MangaId, HttpContext.RequestAborted) is not { } manga)
return NotFound(nameof(MangaId));
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is not { } fetcher)
return BadRequest();
MetadataSearchResult[] searchResults = searchTerm is null ? fetcher.SearchMetadataEntry(manga) : fetcher.SearchMetadataEntry(searchTerm);
return Ok(searchResults);
}
///
/// Links (Metadata-Sites) using Provider-Specific Identifier to
///
/// .Key
/// .Name
/// -Specific ID
///
/// (Metadata-Sites) with does not exist
/// with not found
/// Error during Database Operation
[HttpPost("{MetadataFetcherName}/Link/{MangaId}")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public async Task LinkMangaMetadata (string MangaId, string MetadataFetcherName, [FromBody]string Identifier)
{
if (await context.Mangas.FirstOrDefaultAsync(m => m.Key == MangaId, HttpContext.RequestAborted) is not { } manga)
return NotFound(nameof(MangaId));
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is not { } fetcher)
return BadRequest();
MetadataEntry entry = fetcher.CreateMetadataEntry(manga, Identifier);
context.MetadataEntries.Add(entry);
if(await context.Sync(HttpContext.RequestAborted) is { success: false } result)
return StatusCode(Status500InternalServerError, result.exceptionMessage);
return Ok(entry);
}
///
/// Un-Links (Metadata-Sites) from
///
///
/// (Metadata-Sites) with does not exist
/// with not found
/// No linking and found
/// Error during Database Operation
[HttpPost("{MetadataFetcherName}/Unlink/{MangaId}")]
[ProducesResponseType(Status200OK)]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status412PreconditionFailed, "text/plain")]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public async Task UnlinkMangaMetadata (string MangaId, string MetadataFetcherName)
{
if (await context.Mangas.FirstOrDefaultAsync(m => m.Key == MangaId, HttpContext.RequestAborted) is not { } _)
return NotFound(nameof(MangaId));
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is null)
return BadRequest();
if(context.MetadataEntries.FirstOrDefault(e => e.MangaId == MangaId && e.MetadataFetcherName == MetadataFetcherName) is not { } entry)
return StatusCode(Status412PreconditionFailed, "No entry found");
context.Remove(entry);
if(await context.Sync(HttpContext.RequestAborted) is { success: false } result)
return StatusCode(Status500InternalServerError, result.exceptionMessage);
return Ok();
}
}