Skip to content

Commit

Permalink
feat: add random image metadata endpoint
Browse files Browse the repository at this point in the history
add an endpoint to get the metadata for a random image,
instead of the image itself, also with some basic series metadata,
if appropriate and available.

this endpoint was needed to show the series name on the login screen,
but it can be used for other things too.
  • Loading branch information
revam committed Apr 6, 2023
1 parent 76c2055 commit 952110e
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
42 changes: 42 additions & 0 deletions Shoko.Server/API/v3/Controllers/ImageController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,48 @@ public ActionResult GetRandomImageForType([FromRoute] Image.ImageType imageType)
return File(System.IO.File.OpenRead(path), Mime.GetMimeMapping(path));
}

/// <summary>
/// Returns the metadata for a random image for the <paramref name="imageType"/>.
/// </summary>
/// <param name="imageType">Poster, Fanart, Banner, Thumb</param>
/// <returns>200 on found, 400 if the type or source are invalid</returns>
[HttpGet("Random/{imageType}/Metadata")]
[ProducesResponseType(typeof(Image), 200)]
[ProducesResponseType(400)]
[ProducesResponseType(500)]
public ActionResult<Image> GetRandomImageMetadataForType([FromRoute] Image.ImageType imageType)
{
if (imageType == Image.ImageType.Static)
return BadRequest("Unsupported image type for random image.");

var source = Image.GetRandomImageSource(imageType);
var sourceType = Image.GetImageTypeFromSourceAndType(source, imageType) ?? ImageEntityType.None;
if (sourceType == ImageEntityType.None)
return InternalError("Could not generate a valid image type to fetch.");

// Try 5 times to get a valid image.
var tries = 0;
do
{
var id = Image.GetRandomImageID(sourceType);
if (!id.HasValue)
break;

var path = Image.GetImagePath(sourceType, id.Value);
if (string.IsNullOrEmpty(path))
continue;

var image = new Image(id.Value, sourceType, false, false);
var series = Image.GetFirstSeriesForImage(sourceType, id.Value);
if (series != null)
image.Series = new(series.AnimeSeriesID, series.GetSeriesName());

return image;
} while (tries++ < 5);

return InternalError("Unable to find a random image to send.");
}

public ImageController(ISettingsProvider settingsProvider) : base(settingsProvider)
{
}
Expand Down
118 changes: 118 additions & 0 deletions Shoko.Server/API/v3/Models/Common/Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Shoko.Models.Enums;
using Shoko.Server.Extensions;
using Shoko.Server.ImageDownload;
using Shoko.Server.Models;
using Shoko.Server.Repositories;
using Shoko.Server.Utilities;

Expand Down Expand Up @@ -65,6 +66,13 @@ public class Image
/// </summary>
public bool Disabled { get; set; }

/// <summary>
/// Series info for the image, currently only set when sending a random
/// image.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public ImageSeriesInfo? Series { get; set; } = null;

public Image(int id, ImageEntityType type, bool preferred = false, bool disabled = false) : this(id.ToString(),
type, preferred, disabled)
{
Expand Down Expand Up @@ -537,6 +545,97 @@ internal static ImageSource GetRandomImageSource(ImageType imageType)
};
}

internal static SVR_AnimeSeries? GetFirstSeriesForImage(ImageEntityType imageType, int imageID)
{
switch (imageType)
{
case ImageEntityType.AniDB_Cover:
return RepoFactory.AnimeSeries.GetByAnimeID(imageID);
case ImageEntityType.TvDB_Banner:
{
var tvdbWideBanner = RepoFactory.TvDB_ImageWideBanner.GetByID(imageID);
if (tvdbWideBanner == null)
return null;

var xref = RepoFactory.CrossRef_AniDB_TvDB.GetByTvDBID(tvdbWideBanner.SeriesID)
.FirstOrDefault();
if (xref == null)
return null;

return RepoFactory.AnimeSeries.GetByAnimeID(xref.AniDBID);
}
case ImageEntityType.TvDB_Cover:
{
var tvdbPoster = RepoFactory.TvDB_ImagePoster.GetByID(imageID);
if (tvdbPoster == null)
return null;

var xref = RepoFactory.CrossRef_AniDB_TvDB.GetByTvDBID(tvdbPoster.SeriesID)
.FirstOrDefault();
if (xref == null)
return null;

return RepoFactory.AnimeSeries.GetByAnimeID(xref.AniDBID);
}
case ImageEntityType.TvDB_FanArt:
{
var tvdbFanart = RepoFactory.TvDB_ImageFanart.GetByID(imageID);
if (tvdbFanart == null)
return null;

var xref = RepoFactory.CrossRef_AniDB_TvDB.GetByTvDBID(tvdbFanart.SeriesID)
.FirstOrDefault();
if (xref == null)
return null;

return RepoFactory.AnimeSeries.GetByAnimeID(xref.AniDBID);
}
case ImageEntityType.TvDB_Episode:
{
var tvdbEpisode = RepoFactory.TvDB_Episode.GetByID(imageID);
if (tvdbEpisode == null)
return null;

var xref = RepoFactory.CrossRef_AniDB_TvDB.GetByTvDBID(tvdbEpisode.SeriesID)
.FirstOrDefault();
if (xref == null)
return null;

return RepoFactory.AnimeSeries.GetByAnimeID(xref.AniDBID);
}
case ImageEntityType.MovieDB_FanArt:
{
var tmdbFanart = RepoFactory.MovieDB_Fanart.GetByID(imageID);
if (tmdbFanart == null)
return null;

// This will be slow as HELL. Why don't we have a lookup?
var xref = RepoFactory.CrossRef_AniDB_Other.GetAll()
.FirstOrDefault(xref => xref.CrossRefType == (int)CrossRefType.MovieDB && int.Parse(xref.CrossRefID) == tmdbFanart.MovieId);
if (xref == null)
return null;

return RepoFactory.AnimeSeries.GetByAnimeID(xref.AnimeID);
}
case ImageEntityType.MovieDB_Poster:
{
var tmdbPoster = RepoFactory.MovieDB_Poster.GetByID(imageID);
if (tmdbPoster == null)
return null;

// This will be slow as HELL. Why don't we have a lookup?
var xref = RepoFactory.CrossRef_AniDB_Other.GetAll()
.FirstOrDefault(xref => xref.CrossRefType == (int)CrossRefType.MovieDB && int.Parse(xref.CrossRefID) == tmdbPoster.MovieId);
if (xref == null)
return null;

return RepoFactory.AnimeSeries.GetByAnimeID(xref.AnimeID);
}
default:
return null;
};
}

/// <summary>
/// Image source.
/// </summary>
Expand Down Expand Up @@ -606,6 +705,25 @@ public enum ImageType
Static = 100
}

public class ImageSeriesInfo
{
/// <summary>
/// The shoko series id.
/// </summary>
public int ID { get; set; }

/// <summary>
/// The preferred series name for the user.
/// </summary>
public string Name { get; set; }

public ImageSeriesInfo(int id, string name)
{
ID = id;
Name = name;
}
}

/// <summary>
/// Input models.
/// </summary>
Expand Down

0 comments on commit 952110e

Please sign in to comment.