From 2dd60c4f294f86297ce69490ecdac7e2bf9dba81 Mon Sep 17 00:00:00 2001 From: Mikal Stordal Date: Mon, 20 Nov 2023 21:21:57 +0100 Subject: [PATCH] refactor: update used release group query and add v3 release group endpoint to get used, unused, or all anidb release groups. --- .../ShokoServiceImplementation_Utilities.cs | 4 +- .../API/v3/Controllers/AniDBController.cs | 47 ++++++++++++++++ Shoko.Server/API/v3/Models/Shoko/File.cs | 24 +-------- .../API/v3/Models/Shoko/ReleaseGroup.cs | 37 +++++++++++++ Shoko.Server/Models/SVR_AniDB_File.cs | 12 ++++- .../Cached/AniDB_ReleaseGroupRepository.cs | 53 +++++++++++++------ 6 files changed, 134 insertions(+), 43 deletions(-) create mode 100644 Shoko.Server/API/v3/Controllers/AniDBController.cs create mode 100644 Shoko.Server/API/v3/Models/Shoko/ReleaseGroup.cs diff --git a/Shoko.Server/API/v1/Implementations/ShokoServiceImplementation/ShokoServiceImplementation_Utilities.cs b/Shoko.Server/API/v1/Implementations/ShokoServiceImplementation/ShokoServiceImplementation_Utilities.cs index 83bf269af..cc4dd48b4 100644 --- a/Shoko.Server/API/v1/Implementations/ShokoServiceImplementation/ShokoServiceImplementation_Utilities.cs +++ b/Shoko.Server/API/v1/Implementations/ShokoServiceImplementation/ShokoServiceImplementation_Utilities.cs @@ -77,7 +77,7 @@ private static string ReplaceCaseInsensitive(string input, string search, string private static string RemoveSubgroups(string value) { var originalLength = value.Length; - var releaseGroups = RepoFactory.AniDB_ReleaseGroup.GetAllReleaseGroups(); + var releaseGroups = RepoFactory.AniDB_ReleaseGroup.GetUsedReleaseGroups().Select(r => r.GroupName); foreach (var releaseGroup in releaseGroups) { value = ReplaceCaseInsensitive(value, releaseGroup, string.Empty); @@ -154,7 +154,7 @@ public List SearchAnimeWithFilename(int uid, [FromForm]string qu [HttpGet("ReleaseGroups")] public List GetAllReleaseGroups() { - return RepoFactory.AniDB_ReleaseGroup.GetAllReleaseGroups().ToList(); + return RepoFactory.AniDB_ReleaseGroup.GetUsedReleaseGroups().Select(r => r.GroupName).ToList(); } [HttpGet("File/DeleteMultipleFilesWithPreferences/{userID}")] diff --git a/Shoko.Server/API/v3/Controllers/AniDBController.cs b/Shoko.Server/API/v3/Controllers/AniDBController.cs new file mode 100644 index 000000000..f5fa0778a --- /dev/null +++ b/Shoko.Server/API/v3/Controllers/AniDBController.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Shoko.Server.API.Annotations; +using Shoko.Server.API.v3.Helpers; +using Shoko.Server.API.v3.Models.Common; +using Shoko.Server.API.v3.Models.Shoko; +using Shoko.Server.Repositories; +using Shoko.Server.Settings; + +namespace Shoko.Server.API.v3.Controllers; + +[ApiController] +[Route("/api/v{version:apiVersion}/[controller]")] +[ApiV3] +[Authorize] +public class AniDBController : BaseController +{ + /// + /// Get the known anidb release groups stored in shoko. + /// + /// The page size. Set to 0 to disable pagination. + /// The page index. + /// Include missing release groups. + /// + [HttpGet("ReleaseGroup")] + public ActionResult> GetReleaseGroups( + [FromQuery, Range(0, 1000)] int pageSize = 20, + [FromQuery, Range(1, int.MaxValue)] int page = 1, + [FromQuery] IncludeOnlyFilter includeMissing = IncludeOnlyFilter.False) + { + return includeMissing switch + { + IncludeOnlyFilter.False => RepoFactory.AniDB_ReleaseGroup.GetUsedReleaseGroups() + .ToListResult(g => new ReleaseGroup(g), page, pageSize), + IncludeOnlyFilter.Only => RepoFactory.AniDB_ReleaseGroup.GetUnusedReleaseGroups() + .ToListResult(g => new ReleaseGroup(g), page, pageSize), + _ => RepoFactory.AniDB_ReleaseGroup.GetAll() + .ToListResult(g => new ReleaseGroup(g), page, pageSize), + }; + } + + public AniDBController(ISettingsProvider settingsProvider) : base(settingsProvider) + { + } +} diff --git a/Shoko.Server/API/v3/Models/Shoko/File.cs b/Shoko.Server/API/v3/Models/Shoko/File.cs index db8563004..6bed80b5d 100644 --- a/Shoko.Server/API/v3/Models/Shoko/File.cs +++ b/Shoko.Server/API/v3/Models/Shoko/File.cs @@ -230,10 +230,7 @@ public AniDB(SVR_AniDB_File anidb) { ID = anidb.FileID; Source = ParseFileSource(anidb.File_Source); - ReleaseGroup = new AniDBReleaseGroup - { - ID = anidb.GroupID, Name = anidb.Anime_GroupName, ShortName = anidb.Anime_GroupNameShort - }; + ReleaseGroup = new ReleaseGroup(anidb.ReleaseGroup); ReleaseDate = anidb.File_ReleaseDate == 0 ? null : Commons.Utils.AniDB.GetAniDBDateAsDate(anidb.File_ReleaseDate); @@ -262,7 +259,7 @@ public AniDB(SVR_AniDB_File anidb) /// /// The Release Group. This is usually set, but sometimes is set as "raw/unknown" /// - public AniDBReleaseGroup ReleaseGroup { get; set; } + public ReleaseGroup ReleaseGroup { get; set; } /// /// The file's release date. This is probably not filled in @@ -321,23 +318,6 @@ public AniDB(SVR_AniDB_File anidb) [JsonConverter(typeof(IsoDateTimeConverter))] public DateTime Updated { get; set; } - public class AniDBReleaseGroup - { - /// - /// The Release Group's Name (Unlimited Translation Works) - /// - public string Name { get; set; } - - /// - /// The Release Group's Name (UTW) - /// - public string ShortName { get; set; } - - /// - /// AniDB ID - /// - public int ID { get; set; } - } } public class CrossReferenceIDs diff --git a/Shoko.Server/API/v3/Models/Shoko/ReleaseGroup.cs b/Shoko.Server/API/v3/Models/Shoko/ReleaseGroup.cs new file mode 100644 index 000000000..2e1d18d53 --- /dev/null +++ b/Shoko.Server/API/v3/Models/Shoko/ReleaseGroup.cs @@ -0,0 +1,37 @@ + +using Shoko.Models.Server; + +namespace Shoko.Server.API.v3.Models.Shoko; + + +public class ReleaseGroup +{ + /// + /// AniDB release group ID (69) + /// /// + public int ID { get; set; } + + /// + /// The Release Group's Name (Unlimited Translation Works) + /// + public string Name { get; set; } + + /// + /// The Release Group's Name (UTW) + /// + public string ShortName { get; set; } + + /// + /// Source. Anidb, User, etc. + /// + /// + public string Source { get; set; } + + public ReleaseGroup(AniDB_ReleaseGroup group) + { + ID = group.GroupID; + Name = group.GroupName; + ShortName = group.ShortName; + Source = "AniDB"; + } +} diff --git a/Shoko.Server/Models/SVR_AniDB_File.cs b/Shoko.Server/Models/SVR_AniDB_File.cs index 20823645b..290e1afb5 100644 --- a/Shoko.Server/Models/SVR_AniDB_File.cs +++ b/Shoko.Server/Models/SVR_AniDB_File.cs @@ -33,9 +33,17 @@ public class SVR_AniDB_File : AniDB_File, IAniDBFile [XmlIgnore] public List EpisodeCrossRefs => RepoFactory.CrossRef_File_Episode.GetByHash(Hash); - public string Anime_GroupName => RepoFactory.AniDB_ReleaseGroup.GetByGroupID(GroupID)?.Name; - public string Anime_GroupNameShort => RepoFactory.AniDB_ReleaseGroup.GetByGroupID(GroupID)?.ShortName; + // NOTE: I want to cache it, but i won't for now. not until the anidb files and release groups are stored in a non-cached repo. + public AniDB_ReleaseGroup ReleaseGroup => + RepoFactory.AniDB_ReleaseGroup.GetByGroupID(GroupID) ?? new() + { + GroupID = GroupID, + GroupName = "", + GroupNameShort = "", + }; + public string Anime_GroupName => ReleaseGroup?.Name; + public string Anime_GroupNameShort => ReleaseGroup?.ShortName; public string SubtitlesRAW { diff --git a/Shoko.Server/Repositories/Cached/AniDB_ReleaseGroupRepository.cs b/Shoko.Server/Repositories/Cached/AniDB_ReleaseGroupRepository.cs index 1a816ef67..4507f8a14 100644 --- a/Shoko.Server/Repositories/Cached/AniDB_ReleaseGroupRepository.cs +++ b/Shoko.Server/Repositories/Cached/AniDB_ReleaseGroupRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using NutzCode.InMemoryIndex; using Shoko.Models.Server; using Shoko.Server.Databases; @@ -14,29 +15,47 @@ public AniDB_ReleaseGroup GetByGroupID(int id) return ReadLock(() => GroupIDs.GetOne(id)); } - public IList GetAllReleaseGroups() + const string UsedReleaseGroupsQuery = @"SELECT {g.*} +FROM AniDB_File f +INNER JOIN AniDB_ReleaseGroup g ON f.GroupID = g.GroupID +INNER JOIN CrossRef_File_Episode x ON x.Hash = f.Hash +GROUP BY g.GroupID +ORDER BY g.GroupName ASC"; + + public IReadOnlyList GetUsedReleaseGroups() { - var query = - @"SELECT g.GroupName -FROM AniDB_File a -INNER JOIN AniDB_ReleaseGroup g ON a.GroupID = g.GroupID -INNER JOIN CrossRef_File_Episode xref1 ON xref1.Hash = a.Hash -GROUP BY g.GroupName -ORDER BY count(DISTINCT xref1.AnimeID) DESC, g.GroupName ASC"; - - var result = Lock(() => + var results = Lock(() => { using var session = DatabaseFactory.SessionFactory.OpenSession(); - return session.CreateSQLQuery(query).List(); + return session.CreateSQLQuery(UsedReleaseGroupsQuery).AddEntity("g", typeof(AniDB_ReleaseGroup)) + .List(); }); + return results + .Select(result => (AniDB_ReleaseGroup)result) + .Where(result => !string.Equals(result.GroupName, "raw/unknown", System.StringComparison.InvariantCultureIgnoreCase)) + .ToList(); + } - if (result.Contains("raw/unknown")) - { - result.Remove("raw/unknown"); - } + const string UnusedReleaseGroupsQuery = @"SELECT {g.*} +FROM AniDB_ReleaseGroup g +LEFT JOIN AniDB_File f ON f.GroupID = g.GroupID +WHERE f.GroupID IS NULL +GROUP BY g.GroupID +ORDER BY g.GroupName ASC"; - return result; - } + public IReadOnlyList GetUnusedReleaseGroups() + { + var results = Lock(() => + { + using var session = DatabaseFactory.SessionFactory.OpenSession(); + return session.CreateSQLQuery(UnusedReleaseGroupsQuery).AddEntity("g", typeof(AniDB_ReleaseGroup)) + .List(); + }); + return results + .Select(result => (AniDB_ReleaseGroup)result) + .Where(result => !string.Equals(result.GroupName, "raw/unknown", System.StringComparison.InvariantCultureIgnoreCase)) + .ToList(); + } public override void PopulateIndexes() {