Skip to content

Commit

Permalink
fix: get all relations in APIv3 and plugin abstraction
Browse files Browse the repository at this point in the history
Get all the relations stored locally, even if we only have half of the relation pointers because we haven't updated our database in awhile and only have the newer relations pointing to older shows, etc., without having to update the anidb anime to grab the new relations for the anime with the missing relation pointers.
  • Loading branch information
revam committed Nov 27, 2024
1 parent bba2446 commit 9c7ce5e
Show file tree
Hide file tree
Showing 13 changed files with 234 additions and 81 deletions.
28 changes: 26 additions & 2 deletions Shoko.Plugin.Abstractions/DataModels/IRelatedMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@

using System;

namespace Shoko.Plugin.Abstractions.DataModels;

/// <summary>
/// Related metadata.
/// </summary>
public interface IRelatedMetadata
public interface IRelatedMetadata : IEquatable<IRelatedMetadata>
{
/// <summary>
/// Base entity id.
/// </summary>
int BaseID { get; }

/// <summary>
/// Related entity id.
/// </summary>
Expand All @@ -15,16 +22,33 @@ public interface IRelatedMetadata
/// Relation type.
/// </summary>
RelationType RelationType { get; }

/// <summary>
/// Reverse relation.
/// </summary>
/// <returns>The reversed relation.</returns>
IRelatedMetadata Reversed { get; }
}

/// <summary>
/// Related metadata with entity.
/// </summary>
/// <typeparam name="TMetadata">Related entity type.</typeparam>
public interface IRelatedMetadata<TMetadata> : IMetadata, IRelatedMetadata where TMetadata : IMetadata<int>
public interface IRelatedMetadata<TMetadata> : IMetadata, IRelatedMetadata, IEquatable<IRelatedMetadata<TMetadata>> where TMetadata : IMetadata<int>
{
/// <summary>
/// Base entity, if available.
/// </summary>
TMetadata? Base { get; }

/// <summary>
/// Related entity, if available.
/// </summary>
TMetadata? Related { get; }

/// <summary>
/// Reverse relation.
/// </summary>
/// <returns>The reversed relation.</returns>
new IRelatedMetadata<TMetadata> Reversed { get; }
}
29 changes: 29 additions & 0 deletions Shoko.Plugin.Abstractions/Extensions/RelationTypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

using Shoko.Plugin.Abstractions.DataModels;

namespace Shoko.Plugin.Abstractions.Extensions;

/// <summary>
/// Extensions for the <see cref="RelationType"/> enum.
/// </summary>
public static class RelationTypeExtensions
{
/// <summary>
/// Reverse the relation.
/// </summary>
/// <param name="type">The relation to reverse.</param>
/// <returns>The reversed relation.</returns>
public static RelationType Reverse(this RelationType type)
{
return type switch
{
RelationType.Prequel => RelationType.Sequel,
RelationType.Sequel => RelationType.Prequel,
RelationType.MainStory => RelationType.SideStory,
RelationType.SideStory => RelationType.MainStory,
RelationType.FullStory => RelationType.Summary,
RelationType.Summary => RelationType.FullStory,
_ => type
};
}
}
2 changes: 1 addition & 1 deletion Shoko.Plugin.Abstractions/Shoko.Plugin.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<RepositoryUrl>https://github.com/ShokoAnime/ShokoServer</RepositoryUrl>
<PackageTags>plugins, shoko, anime, metadata, tagging</PackageTags>
<PackageReleaseNotes>Renamer Rewrite</PackageReleaseNotes>
<Version>4.1.0-beta1</Version>
<Version>4.1.0-beta2</Version>
<Configurations>Debug;Release;Benchmarks</Configurations>
<Platforms>AnyCPU;x64</Platforms>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
Expand Down
14 changes: 9 additions & 5 deletions Shoko.Server/API/v3/Controllers/GroupController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;
using Shoko.Plugin.Abstractions.DataModels;
using Shoko.Server.API.Annotations;
using Shoko.Server.API.v3.Helpers;
using Shoko.Server.API.v3.Models.Common;
Expand Down Expand Up @@ -285,12 +286,15 @@ public ActionResult<List<SeriesRelation>> GetShokoRelationsBySeriesID([FromRoute
.ToHashSet();

// TODO: Replace with a more generic implementation capable of supplying relations from more than just AniDB.
return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(animeIds)
.Select(relation =>
(relation, relatedSeries: RepoFactory.AnimeSeries.GetByAnimeID(relation.RelatedAnimeID)))
return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(animeIds).OfType<IRelatedMetadata>()
.Concat(RepoFactory.AniDB_Anime_Relation.GetByRelatedAnimeID(animeIds).OfType<IRelatedMetadata>().Select(a => a.Reversed))
.Distinct()
.Select(relation => (relation, relatedSeries: RepoFactory.AnimeSeries.GetByAnimeID(relation.RelatedID)))
.Where(tuple => tuple.relatedSeries != null && animeIds.Contains(tuple.relatedSeries.AniDB_ID))
.Select(tuple => new SeriesRelation(HttpContext, tuple.relation, seriesDict[tuple.relation.AnimeID],
tuple.relatedSeries))
.OrderBy(tuple => tuple.relation.BaseID)
.ThenBy(tuple => tuple.relation.RelatedID)
.ThenBy(tuple => tuple.relation.RelationType)
.Select(tuple => new SeriesRelation(tuple.relation, seriesDict[tuple.relation.BaseID], tuple.relatedSeries))
.ToList();
}

Expand Down
40 changes: 29 additions & 11 deletions Shoko.Server/API/v3/Controllers/SeriesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,15 @@ public ActionResult<List<SeriesRelation>> GetShokoRelationsBySeriesID([FromRoute
}

// TODO: Replace with a more generic implementation capable of supplying relations from more than just AniDB.
return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(series.AniDB_ID)
.Select(relation =>
(relation, relatedSeries: RepoFactory.AnimeSeries.GetByAnimeID(relation.RelatedAnimeID)))
return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(series.AniDB_ID).OfType<IRelatedMetadata>()
.Concat(RepoFactory.AniDB_Anime_Relation.GetByRelatedAnimeID(series.AniDB_ID).OfType<IRelatedMetadata>().Select(a => a.Reversed))
.Distinct()
.Select(relation => (relation, relatedSeries: RepoFactory.AnimeSeries.GetByAnimeID(relation.RelatedID)))
.Where(tuple => tuple.relatedSeries != null)
.Select(tuple => new SeriesRelation(HttpContext, tuple.relation, series, tuple.relatedSeries))
.OrderBy(tuple => tuple.relation.BaseID)
.ThenBy(tuple => tuple.relation.RelatedID)
.ThenBy(tuple => tuple.relation.RelationType)
.Select(tuple => new SeriesRelation(tuple.relation, series, tuple.relatedSeries))
.ToList();
}

Expand Down Expand Up @@ -453,9 +457,13 @@ public ActionResult<ListResult<SeriesRelation>> GetAnidbRelations([FromQuery, Ra
[FromQuery, Range(1, int.MaxValue)] int page = 1)
{
return RepoFactory.AniDB_Anime_Relation.GetAll()
.OrderBy(a => a.AnimeID)
.ThenBy(a => a.RelatedAnimeID)
.ToListResult(relation => new SeriesRelation(HttpContext, relation), page, pageSize);
.OfType<IRelatedMetadata>()
.SelectMany<IRelatedMetadata, IRelatedMetadata>(a => [a, a.Reversed])
.Distinct()
.OrderBy(a => a.BaseID)
.ThenBy(a => a.RelatedID)
.ThenBy(a => a.RelationType)
.ToListResult(relation => new SeriesRelation(relation), page, pageSize);
}

/// <summary>
Expand Down Expand Up @@ -541,7 +549,12 @@ public ActionResult<ListResult<SeriesRelation>> GetAnidbRelations([FromQuery, Ra
return InternalError(AnidbNotFoundForSeriesID);
}

return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(anidb.AnimeID)
return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(anidb.AnimeID).OfType<IRelatedMetadata>()
.Concat(RepoFactory.AniDB_Anime_Relation.GetByRelatedAnimeID(anidb.AnimeID).OfType<IRelatedMetadata>().Select(a => a.Reversed))
.Distinct()
.OrderBy(a => a.BaseID)
.ThenBy(a => a.RelatedID)
.ThenBy(a => a.RelationType)
.Select(relation => new Series.AniDB(relation))
.ToList();
}
Expand Down Expand Up @@ -571,8 +584,13 @@ public ActionResult<List<SeriesRelation>> GetAnidbRelationsBySeriesID([FromRoute
return InternalError(AnidbNotFoundForSeriesID);
}

return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(anidb.AnimeID)
.Select(relation => new SeriesRelation(HttpContext, relation, series))
return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(anidb.AnimeID).OfType<IRelatedMetadata>()
.Concat(RepoFactory.AniDB_Anime_Relation.GetByRelatedAnimeID(anidb.AnimeID).OfType<IRelatedMetadata>().Select(a => a.Reversed))
.Distinct()
.OrderBy(a => a.BaseID)
.ThenBy(a => a.RelatedID)
.ThenBy(a => a.RelationType)
.Select(relation => new SeriesRelation(relation, series))
.ToList();
}

Expand Down Expand Up @@ -823,7 +841,7 @@ public ActionResult<List<SeriesRelation>> GetAnidbRelationsByAnidbID([FromRoute]
}

return RepoFactory.AniDB_Anime_Relation.GetByAnimeID(anidbID)
.Select(relation => new SeriesRelation(HttpContext, relation))
.Select(relation => new SeriesRelation(relation))
.ToList();
}

Expand Down
41 changes: 6 additions & 35 deletions Shoko.Server/API/v3/Models/Common/SeriesRelation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,15 @@ public class SeriesRelation
[Required]
public string Source { get; set; }

public SeriesRelation(HttpContext context, AniDB_Anime_Relation relation, AnimeSeries series = null,
public SeriesRelation(IRelatedMetadata relation, AnimeSeries series = null,
AnimeSeries relatedSeries = null)
{
if (series == null)
{
series = RepoFactory.AnimeSeries.GetByAnimeID(relation.AnimeID);
}
series ??= RepoFactory.AnimeSeries.GetByAnimeID(relation.BaseID);
relatedSeries ??= RepoFactory.AnimeSeries.GetByAnimeID(relation.RelatedID);

if (relatedSeries == null)
{
relatedSeries = RepoFactory.AnimeSeries.GetByAnimeID(relation.RelatedAnimeID);
}

IDs = new RelationIDs { AniDB = relation.AnimeID, Shoko = series?.AnimeSeriesID };
RelatedIDs = new RelationIDs { AniDB = relation.RelatedAnimeID, Shoko = relatedSeries?.AnimeSeriesID };
Type = ((IRelatedMetadata)relation).RelationType;
IDs = new RelationIDs { AniDB = relation.BaseID, Shoko = series?.AnimeSeriesID };
RelatedIDs = new RelationIDs { AniDB = relation.RelatedID, Shoko = relatedSeries?.AnimeSeriesID };
Type = relation.RelationType;
Source = "AniDB";
}

Expand All @@ -72,25 +65,3 @@ public class RelationIDs
public int? AniDB { get; set; }
}
}

public static class RelationExtensions
{
/// <summary>
/// Reverse the relation.
/// </summary>
/// <param name="type">The relation to reverse.</param>
/// <returns>The reversed relation.</returns>
public static RelationType Reverse(this RelationType type)
{
return type switch
{
RelationType.Prequel => RelationType.Sequel,
RelationType.Sequel => RelationType.Prequel,
RelationType.MainStory => RelationType.SideStory,
RelationType.SideStory => RelationType.MainStory,
RelationType.FullStory => RelationType.Summary,
RelationType.Summary => RelationType.FullStory,
_ => type
};
}
}
10 changes: 5 additions & 5 deletions Shoko.Server/API/v3/Models/Shoko/Series.cs
Original file line number Diff line number Diff line change
Expand Up @@ -519,13 +519,13 @@ public AniDB(SVR_AniDB_Anime anime, SVR_AnimeSeries? series = null, bool include
public AniDB(ResponseAniDBTitles.Anime result, SVR_AnimeSeries? series = null, bool includeTitles = true)
: this(result.AnimeID, includeTitles, series) { }

public AniDB(SVR_AniDB_Anime_Relation relation, SVR_AnimeSeries? series = null, bool includeTitles = true)
: this(relation.RelatedAnimeID, includeTitles, series)
public AniDB(IRelatedMetadata relation, SVR_AnimeSeries? series = null, bool includeTitles = true)
: this(relation.RelatedID, includeTitles, series)
{
Relation = ((IRelatedMetadata)relation).RelationType;
Relation = relation.RelationType;
// If the other anime is present we assume they're of the same kind. Be it restricted or unrestricted.
if (Type == SeriesType.Unknown && TitleHelper.SearchAnimeID(relation.RelatedAnimeID) is not null)
Restricted = RepoFactory.AniDB_Anime.GetByAnimeID(relation.AnimeID) is { IsRestricted: true };
if (Type == SeriesType.Unknown && TitleHelper.SearchAnimeID(relation.RelatedID) is not null)
Restricted = RepoFactory.AniDB_Anime.GetByAnimeID(relation.BaseID) is { IsRestricted: true };
}

public AniDB(AniDB_Anime_Similar similar, SVR_AnimeSeries? series = null, bool includeTitles = true)
Expand Down
3 changes: 2 additions & 1 deletion Shoko.Server/Databases/MySQL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace Shoko.Server.Databases;
public class MySQL : BaseDatabase<MySqlConnection>
{
public override string Name { get; } = "MySQL";
public override int RequiredVersion { get; } = 141;
public override int RequiredVersion { get; } = 142;

private List<DatabaseCommand> createVersionTable = new()
{
Expand Down Expand Up @@ -854,6 +854,7 @@ public class MySQL : BaseDatabase<MySqlConnection>
new(141, 2, "ALTER TABLE `TMDB_Movie` ADD COLUMN `ProductionCountries` VARCHAR(32) NULL DEFAULT NULL;"),
new(141, 3, "ALTER TABLE `TMDB_Show` ADD COLUMN `Keywords` VARCHAR(512) NULL DEFAULT NULL;"),
new(141, 4, "ALTER TABLE `TMDB_Show` ADD COLUMN `ProductionCountries` VARCHAR(32) NULL DEFAULT NULL;"),
new(142, 1, "ALTER TABLE `AniDB_Anime_Relation` ADD INDEX `IX_AniDB_Anime_Relation_RelatedAnimeID` (`RelatedAnimeID` ASC);"),
};

private DatabaseCommand linuxTableVersionsFix = new("RENAME TABLE versions TO Versions;");
Expand Down
3 changes: 2 additions & 1 deletion Shoko.Server/Databases/SQLServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Shoko.Server.Databases;
public class SQLServer : BaseDatabase<SqlConnection>
{
public override string Name { get; } = "SQLServer";
public override int RequiredVersion { get; } = 133;
public override int RequiredVersion { get; } = 134;

public override void BackupDatabase(string fullfilename)
{
Expand Down Expand Up @@ -784,6 +784,7 @@ public override bool HasVersionsTable()
new DatabaseCommand(133, 2, "ALTER TABLE TMDB_Movie ADD ProductionCountries NVARCHAR(32) NULL DEFAULT NULL;"),
new DatabaseCommand(133, 3, "ALTER TABLE TMDB_Show ADD Keywords NVARCHAR(512) NULL DEFAULT NULL;"),
new DatabaseCommand(133, 4, "ALTER TABLE TMDB_Show ADD ProductionCountries NVARCHAR(32) NULL DEFAULT NULL;"),
new DatabaseCommand(134, 1, "CREATE INDEX IX_AniDB_Anime_Relation_RelatedAnimeID on AniDB_Anime_Relation(RelatedAnimeID);"),
};

private static void AlterImdbMovieIDType()
Expand Down
3 changes: 2 additions & 1 deletion Shoko.Server/Databases/SQLite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class SQLite : BaseDatabase<SqliteConnection>
{
public override string Name => "SQLite";

public override int RequiredVersion => 125;
public override int RequiredVersion => 126;

public override void BackupDatabase(string fullfilename)
{
Expand Down Expand Up @@ -779,6 +779,7 @@ public override void CreateDatabase()
new(125, 2, "ALTER TABLE TMDB_Movie ADD COLUMN ProductionCountries TEXT NULL DEFAULT NULL;"),
new(125, 3, "ALTER TABLE TMDB_Show ADD COLUMN Keywords TEXT NULL DEFAULT NULL;"),
new(125, 4, "ALTER TABLE TMDB_Show ADD COLUMN ProductionCountries TEXT NULL DEFAULT NULL;"),
new(126, 1, "CREATE INDEX IX_AniDB_Anime_Relation_RelatedAnimeID on AniDB_Anime_Relation(RelatedAnimeID);"),
};

private static Tuple<bool, string> MigrateRenamers(object connection)
Expand Down
8 changes: 7 additions & 1 deletion Shoko.Server/Models/SVR_AniDB_Anime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,13 @@ public List<CrossRef_AniDB_MAL> GetCrossRefMAL()
IImageMetadata? ISeries.DefaultPoster => this.GetImageMetadata();

IReadOnlyList<IRelatedMetadata<ISeries>> ISeries.RelatedSeries =>
RepoFactory.AniDB_Anime_Relation.GetByAnimeID(AnimeID);
RepoFactory.AniDB_Anime_Relation.GetByAnimeID(AnimeID).OfType<IRelatedMetadata<ISeries>>()
.Concat(RepoFactory.AniDB_Anime_Relation.GetByRelatedAnimeID(AnimeID).OfType<IRelatedMetadata<ISeries>>().Select(a => a.Reversed))
.Distinct()
.OrderBy(a => a.BaseID)
.ThenBy(a => a.RelatedID)
.ThenBy(a => a.RelationType)
.ToList();

IReadOnlyList<IRelatedMetadata<IMovie>> ISeries.RelatedMovies => [];

Expand Down
Loading

0 comments on commit 9c7ce5e

Please sign in to comment.