diff --git a/API/Controllers/MetadataFetcherController.cs b/API/Controllers/MetadataFetcherController.cs
index 62c7b86..33f67b0 100644
--- a/API/Controllers/MetadataFetcherController.cs
+++ b/API/Controllers/MetadataFetcherController.cs
@@ -4,6 +4,7 @@ using API.Schema.MetadataFetchers;
using Asp.Versioning;
using log4net;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using static Microsoft.AspNetCore.Http.StatusCodes;
// ReSharper disable InconsistentNaming
@@ -38,45 +39,94 @@ public class MetadataFetcherController(PgsqlContext context, ILog Log) : Control
}
///
- /// Tries linking a Manga to a Metadata-Provider-Site
+ /// Searches Metadata-Provider for Manga-Metadata
///
+ /// Instead of using the Manga for search, use a specific term
///
/// Metadata-fetcher with Name does not exist
/// Manga with ID not found
- /// Could not find Entry on Metadata-Provider for Manga
- /// Error during Database Operation
- [HttpPost("{MetadataFetcherName}/{MangaId}/TryLink")]
- [ProducesResponseType(Status200OK, "application/json")]
+ [HttpPost("{MetadataFetcherName}/SearchManga/{MangaId}")]
+ [ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
- [ProducesResponseType(Status417ExpectationFailed)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult LinkMangaToMetadataFetcher(string MangaId, string MetadataFetcherName)
+ public IActionResult SearchMangaMetadata(string MangaId, string MetadataFetcherName, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]string? searchTerm = null)
{
if(context.Mangas.Find(MangaId) is not { } manga)
return NotFound();
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.MetadataFetcherName == MetadataFetcherName) is not { } fetcher)
return BadRequest();
- if (!fetcher.TryGetMetadataEntry(manga, out MetadataEntry? entry))
- {
- return StatusCode(Status417ExpectationFailed, "Metadata entry not found");
- }
+ MetadataSearchResult[] searchResults = searchTerm is null ? fetcher.SearchMetadataEntry(manga) : fetcher.SearchMetadataEntry(searchTerm);
+ return Ok(searchResults);
+ }
+
+ ///
+ /// Links Metadata-Provider using Provider-Specific Identifier to Manga
+ ///
+ ///
+ /// Metadata-fetcher with Name does not exist
+ /// Manga with ID not found
+ /// Error during Database Operation
+ [HttpPost("{MetadataFetcherName}/Link/{MangaId}")]
+ [ProducesResponseType(Status200OK)]
+ [ProducesResponseType(Status400BadRequest)]
+ [ProducesResponseType(Status404NotFound)]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult LinkMangaMetadata(string MangaId, string MetadataFetcherName, [FromBody]string Identifier)
+ {
+ if(context.Mangas.Find(MangaId) is not { } manga)
+ return NotFound();
+ if(Tranga.MetadataFetchers.FirstOrDefault(f => f.MetadataFetcherName == MetadataFetcherName) is not { } fetcher)
+ return BadRequest();
+ MetadataEntry entry = fetcher.CreateMetadataEntry(manga, Identifier);
+
try
{
- //Unlink previous metadata-entries
- IQueryable metadataEntries = context.MetadataEntries.Where(e => e.MangaId == MangaId && e.MetadataFetcherName == MetadataFetcherName);
- context.MetadataEntries.RemoveRange(metadataEntries);
- //Add new metadata-entry
context.MetadataEntries.Add(entry);
context.SaveChanges();
- return Ok(entry);
}
catch (Exception e)
{
Log.Error(e);
return StatusCode(500, e.Message);
}
+ return Ok();
+ }
+
+ ///
+ /// Un-Links Metadata-Provider using Provider-Specific Identifier to Manga
+ ///
+ ///
+ /// Metadata-fetcher with Name does not exist
+ /// Manga with ID not found
+ /// No Entry linking Manga and Metadata-Provider found
+ /// Error during Database Operation
+ [HttpPost("{MetadataFetcherName}/Unlink/{MangaId}")]
+ [ProducesResponseType(Status200OK)]
+ [ProducesResponseType(Status400BadRequest)]
+ [ProducesResponseType(Status404NotFound)]
+ [ProducesResponseType(Status412PreconditionFailed, "text/plain")]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult UnlinkMangaMetadata(string MangaId, string MetadataFetcherName)
+ {
+ if(context.Mangas.Find(MangaId) is not { } manga)
+ return NotFound();
+ if(Tranga.MetadataFetchers.FirstOrDefault(f => f.MetadataFetcherName == MetadataFetcherName) is not { } fetcher)
+ return BadRequest();
+ MetadataEntry? entry = context.MetadataEntries.FirstOrDefault(e => e.MangaId == MangaId && e.MetadataFetcherName == MetadataFetcherName);
+ if (entry is null)
+ return StatusCode(Status412PreconditionFailed, "No entry found");
+ try
+ {
+ context.MetadataEntries.Remove(entry);
+ context.SaveChanges();
+ }
+ catch (Exception e)
+ {
+ Log.Error(e);
+ return StatusCode(500, e.Message);
+ }
+ return Ok();
}
///
diff --git a/API/Schema/MetadataFetchers/MetadataFetcher.cs b/API/Schema/MetadataFetchers/MetadataFetcher.cs
index 48b7c07..9952f41 100644
--- a/API/Schema/MetadataFetchers/MetadataFetcher.cs
+++ b/API/Schema/MetadataFetchers/MetadataFetcher.cs
@@ -22,14 +22,13 @@ public abstract class MetadataFetcher
{
this.MetadataFetcherName = metadataFetcherName;
}
-
- public abstract MetadataEntry? FindLinkedMetadataEntry(Manga manga);
- public bool TryGetMetadataEntry(Manga manga, [NotNullWhen(true)] out MetadataEntry? metadataEntry)
- {
- metadataEntry = FindLinkedMetadataEntry(manga);
- return metadataEntry != null;
- }
+ internal MetadataEntry CreateMetadataEntry(Manga manga, string identifier) =>
+ new (this, manga, identifier);
+
+ public abstract MetadataSearchResult[] SearchMetadataEntry(Manga manga);
+
+ public abstract MetadataSearchResult[] SearchMetadataEntry(string searchTerm);
///
/// Updates the Manga linked in the MetadataEntry
diff --git a/API/Schema/MetadataFetchers/MetadataSearchResult.cs b/API/Schema/MetadataFetchers/MetadataSearchResult.cs
new file mode 100644
index 0000000..abdbb33
--- /dev/null
+++ b/API/Schema/MetadataFetchers/MetadataSearchResult.cs
@@ -0,0 +1,3 @@
+namespace API.Schema.MetadataFetchers;
+
+public record MetadataSearchResult(string Identifier, string Name, string Url, string? Description = null, string? CoverUrl = null);
\ No newline at end of file
diff --git a/API/Schema/MetadataFetchers/MyAnimeList.cs b/API/Schema/MetadataFetchers/MyAnimeList.cs
index 2497c98..a350adb 100644
--- a/API/Schema/MetadataFetchers/MyAnimeList.cs
+++ b/API/Schema/MetadataFetchers/MyAnimeList.cs
@@ -10,7 +10,7 @@ public class MyAnimeList : MetadataFetcher
private static readonly Jikan Jikan = new ();
private static readonly Regex GetIdFromUrl = new(@"https?:\/\/myanimelist\.net\/manga\/([0-9]+)\/?.*");
- public override MetadataEntry? FindLinkedMetadataEntry(Manga manga)
+ public override MetadataSearchResult[] SearchMetadataEntry(Manga manga)
{
if (manga.Links.Any(link => link.LinkProvider.Equals("MyAnimeList", StringComparison.InvariantCultureIgnoreCase)))
{
@@ -19,14 +19,23 @@ public class MyAnimeList : MetadataFetcher
if (m.Success && m.Groups[1].Success)
{
long id = long.Parse(m.Groups[1].Value);
- return new MetadataEntry(this, manga, id.ToString()!);
+ JikanDotNet.Manga data = Jikan.GetMangaAsync(id).Result.Data;
+ return [new MetadataSearchResult(id.ToString(), data.Titles.First().Title, data.Url, data.Synopsis)];
}
}
- ICollection resultData = Jikan.SearchMangaAsync(manga.Name).Result.Data;
+ return SearchMetadataEntry(manga.Name);
+ }
+
+ public override MetadataSearchResult[] SearchMetadataEntry(string searchTerm)
+ {
+
+ ICollection resultData = Jikan.SearchMangaAsync(searchTerm).Result.Data;
if (resultData.Count < 1)
- return null;
- return new MetadataEntry(this, manga, resultData.First().MalId.ToString());
+ return [];
+ return resultData.Select(data =>
+ new MetadataSearchResult(data.MalId.ToString(), data.Titles.First().Title, data.Url, data.Synopsis))
+ .ToArray();
}
///