diff --git a/Jellyfin.Plugin.Bangumi/Archive/ArchiveData.cs b/Jellyfin.Plugin.Bangumi/Archive/ArchiveData.cs index bf4b120..5c54443 100644 --- a/Jellyfin.Plugin.Bangumi/Archive/ArchiveData.cs +++ b/Jellyfin.Plugin.Bangumi/Archive/ArchiveData.cs @@ -28,5 +28,9 @@ public class ArchiveData(IApplicationPaths paths) public ArchiveStore Person => new(BasePath, "person.jsonlines"); - public SubjectEpisodeRelation SubjectEpisode => new(this); + public SubjectRelations SubjectRelations => new(this); + + public SubjectEpisodeRelation SubjectEpisodeRelation => new(this); + + public SubjectPersonRelation SubjectPersonRelation => new(this); } \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/Archive/Data/RelatedPerson.cs b/Jellyfin.Plugin.Bangumi/Archive/Data/RelatedPerson.cs new file mode 100644 index 0000000..68f31b3 --- /dev/null +++ b/Jellyfin.Plugin.Bangumi/Archive/Data/RelatedPerson.cs @@ -0,0 +1,202 @@ +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Jellyfin.Plugin.Bangumi.Archive.Data; + +public class RelatedPerson +{ + [JsonPropertyName("person_id")] + public int PersonId { get; set; } + + [JsonPropertyName("position")] + public short Position { get; set; } + + public async Task ToRelatedPerson(ArchiveData archive) + { + var person = await archive.Person.FindById(PersonId); + + return new Model.RelatedPerson + { + Id = PersonId, + Type = (int)(person?.Type ?? default), + Name = person?.Name ?? "", + Career = person?.Career, + + // https://github.com/bangumi/common/blob/master/subject_staffs.yml + Relation = Position switch + { + 1 => "原作", + 2 => "导演", + 3 => "脚本", + 4 => "分镜", + 5 => "演出", + 6 => "音乐", + 7 => "人物原案", + 8 => "人物设定", + 9 => "构图", + 10 => "系列构成", + 11 => "美术监督", + 13 => "色彩设计", + 14 => "总作画监督", + 15 => "作画监督", + 16 => "机械设定", + 17 => "摄影监督", + 18 => "监修", + 19 => "道具设计", + 20 => "原画", + 21 => "第二原画", + 22 => "动画检查", + 23 => "助理制片人", + 24 => "制作助理", + 25 => "背景美术", + 26 => "色彩指定", + 27 => "数码绘图", + 28 => "剪辑", + 29 => "原案", + 30 => "主题歌编曲", + 31 => "主题歌作曲", + 32 => "主题歌作词", + 33 => "主题歌演出", + 34 => "插入歌演出", + 35 => "企画", + 36 => "企划制作人", + 37 => "制作管理", + 38 => "宣传", + 39 => "录音", + 40 => "录音助理", + 41 => "系列监督", + 42 => "製作", + 43 => "设定", + 44 => "音响监督", + 45 => "音响", + 46 => "音效", + 47 => "特效", + 48 => "配音监督", + 49 => "联合导演", + 50 => "背景设定", + 51 => "补间动画", + 52 => "执行制片人", + 53 => "助理制片人", + 54 => "制片人", + 55 => "音乐助理", + 56 => "制作进行", // 管理动画的制作时程、协调各部门作业、回收作画原稿等 + 57 => "演员监督", + 58 => "总制片人", + 59 => "联合制片人", + 60 => "台词编辑", + 61 => "后期制片协调", + 62 => "制作助理", + 63 => "制作", + 64 => "制作协调", + 65 => "音乐制作", + 66 => "特别鸣谢", + 67 => "动画制作", + 69 => "CG 导演", + 70 => "机械作画监督", + 71 => "美术设计", + 72 => "副导演", + 73 => "OP・ED 分镜", + 74 => "总导演", + 75 => "3DCG", + 76 => "制作协力", + 77 => "动作作画监督", + 80 => "监制", + 81 => "协力", + 82 => "摄影", + 83 => "制作进行协力", + 84 => "设定制作", // 有时需要额外的设计工作,联系负责部门并监督工作确保交付 + 85 => "音乐制作人", + 86 => "3DCG 导演", + 87 => "动画制片人", + 88 => "特效作画监督", + 90 => "作画监督助理", + 92 => "主动画师", + 1001 => "开发", + 1002 => "发行", + 1003 => "游戏设计师", + 1004 => "剧本", + 1005 => "美工", + 1006 => "音乐", + 1007 => "关卡设计", + 1008 => "人物设定", + 1009 => "主题歌作曲", + 1010 => "主题歌作词", + 1011 => "主题歌演出", + 1012 => "插入歌演出", + 1013 => "原画", + 1014 => "动画制作", + 1015 => "原作", + 1016 => "导演", + 1017 => "动画监督", + 1018 => "制作总指挥", + 1019 => "QC", + 1020 => "动画剧本", + 1021 => "程序", + 1022 => "协力", + 1023 => "CG 监修", + 1024 => "SD原画", + 1025 => "背景", + 1026 => "监修", + 1027 => "系列构成", + 1028 => "企画", + 1029 => "机械设定", + 1030 => "音响监督", + 1031 => "作画监督", + 1032 => "制作人", + 2001 => "作者", + 2002 => "作画", + 2003 => "插图", + 2004 => "出版社", + 2005 => "连载杂志", + 2006 => "译者", + 2007 => "原作", + 2008 => "客串", + 2009 => "人物原案", + 2010 => "脚本", + 2011 => "书系", + 2012 => "出品方", + 3001 => "艺术家", + 3002 => "制作人", + 3003 => "作曲", + 3004 => "厂牌", + 3005 => "原作", + 3006 => "作词", + 3007 => "录音", + 3008 => "编曲", + 3009 => "插图", + 3010 => "脚本", + 3011 => "出版方", + 3012 => "母带制作", + 3013 => "混音", + 3014 => "乐器", + 3015 => "声乐", + 4001 => "原作", + 4002 => "导演", + 4003 => "编剧", + 4004 => "音乐", + 4005 => "执行制片人", + 4006 => "共同执行制作", + 4007 => "制片人/制作人", + 4008 => "监制", + 4009 => "副制作人/制作顾问", + 4010 => "故事", + 4011 => "编审", + 4012 => "剪辑", + 4013 => "创意总监", + 4014 => "摄影", + 4015 => "主题歌演出", + 4016 => "主演", + 4017 => "配角", + 4018 => "制作", + 4019 => "出品", + _ => null + } + }; + } +} + +public class RelatedPersonRaw : RelatedPerson +{ + [JsonPropertyName("subject_id")] + public int SubjectId { get; set; } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/Archive/Data/RelatedSubject.cs b/Jellyfin.Plugin.Bangumi/Archive/Data/RelatedSubject.cs new file mode 100644 index 0000000..9534150 --- /dev/null +++ b/Jellyfin.Plugin.Bangumi/Archive/Data/RelatedSubject.cs @@ -0,0 +1,88 @@ +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Jellyfin.Plugin.Bangumi.Archive.Data; + +public class RelatedSubject +{ + [JsonPropertyName("related_subject_id")] + public int RelatedSubjectId { get; set; } + + [JsonPropertyName("relation_type")] + public short RelationType { get; set; } + + public async Task ToRelatedSubject(ArchiveData archive) + { + var subject = await archive.Subject.FindById(RelatedSubjectId); + + return new Model.RelatedSubject + { + Id = RelatedSubjectId, + Type = subject?.Type ?? default, + OriginalNameRaw = subject?.OriginalName ?? "", + ChineseNameRaw = subject?.ChineseName, + + // https://github.com/bangumi/common/blob/master/subject_relations.yml + Relation = RelationType switch + { + 1 => "改编", // 同系列不同平台作品(如柯南漫画与动画版) + 2 => "前传", // 发生在故事之前 + 3 => "续集", // 发生在故事之后 + 4 => "总集篇", // 对故事的概括版本 + 5 => "全集", // 相对于剧场版/总集篇的完整故事 + 6 => "番外篇", + 7 => "角色出演", // 相同角色,没有关联的故事 + 8 => "相同世界观", // 发生在同一个世界观/时间线下,不同的出演角色 + 9 => "不同世界观", // 相同的主演角色,不同的世界观/时间线设定 + 10 => "不同演绎", // 相同设定、角色,不同的演绎方式(如EVA原作与新剧场版) + 11 => "衍生", // 世界观相同,角色主线与有关联或来自主线,但又非主线的主角们 + 12 => "主线故事", + 14 => "联动", // 出现了被关联作品中的角色 + 99 => "其他", + 1002 => "系列", + 1003 => "单行本", + 1004 => "画集", + 1005 => "前传", // 发生在故事之前 + 1006 => "续集", // 发生在故事之后 + 1007 => "番外篇", + 1008 => "主线故事", + 1010 => "不同版本", + 1011 => "角色出演", // 相同角色,没有关联的故事 + 1012 => "相同世界观", // 发生在同一个世界观/时间线下,不同的出演角色 + 1013 => "不同世界观", // 相同的出演角色,不同的世界观/时间线设定 + 1014 => "联动", // 出现了被关联作品中的角色 + 1099 => "其他", + 3001 => "原声集", + 3002 => "角色歌", + 3003 => "片头曲", + 3004 => "片尾曲", + 3005 => "插入歌", + 3006 => "印象曲", + 3007 => "广播剧", + 3099 => "其他", + 4002 => "前传", // 发生在故事之前/或作品发售之前 + 4003 => "续集", // 发生在故事之后/或作品发售之后 + 4006 => "外传", + 4007 => "角色出演", // 相同角色,没有关联的故事 + 4008 => "相同世界观", // 发生在同一个世界观/时间线下,不同的出演角色 + 4009 => "不同世界观", // 相同的出演角色,不同的世界观/时间线设定 + 4010 => "不同演绎", // 相同设定、角色,不同的演绎方式 + 4011 => "不同版本", // 相同故事、角色,画面、音乐或系统改进 + 4012 => "主线故事", + 4013 => "主版本", // 游戏最初发售时的版本 + 4014 => "联动", // 出现了被关联作品中的角色 + 4015 => "扩展包", + 4016 => "合集", // 收录本作品的合集条目 + 4017 => "收录作品", // 合集条目中收录的作品 + 4099 => "其他", + _ => null + } + }; + } +} + +public class RelatedSubjectRaw : RelatedSubject +{ + [JsonPropertyName("subject_id")] + public int SubjectId { get; set; } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/Archive/Relation/SubjectPersonRelation.cs b/Jellyfin.Plugin.Bangumi/Archive/Relation/SubjectPersonRelation.cs new file mode 100644 index 0000000..96081d7 --- /dev/null +++ b/Jellyfin.Plugin.Bangumi/Archive/Relation/SubjectPersonRelation.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Plugin.Bangumi.Archive.Data; + +namespace Jellyfin.Plugin.Bangumi.Archive.Relation; + +public class SubjectPersonRelation(ArchiveData archive) +{ + private const string FileName = "subject_person.map"; + + private readonly Dictionary> _mapping = new(); + + private bool _initialized; + + private string FilePath => Path.Join(archive.BasePath, FileName); + + public async Task GenerateIndex(ZipArchive zipStream, CancellationToken token) + { + var entry = zipStream.GetEntry("subject-persons.jsonlines"); + if (entry == null) return; + await using var stream = entry.Open(); + using var reader = new StreamReader(stream, Encoding.UTF8); + + while (await reader.ReadLineAsync(token) is { } line) + { + var item = JsonSerializer.Deserialize(line, Constants.JsonSerializerOptions); + if (item == null) continue; + if (!_mapping.ContainsKey(item.SubjectId)) + _mapping.Add(item.SubjectId, []); + _mapping[item.SubjectId].Add(item); + } + + await Save(); + } + + public async Task Ready() + { + await Load(); + return _mapping.Count > 0; + } + + public async Task> Get(int subjectId) + { + await Load(); + if (!_mapping.TryGetValue(subjectId, out var rawList)) return []; + + var relatedPersons = new List(); + foreach (var relatedPerson in rawList) relatedPersons.Add(await relatedPerson.ToRelatedPerson(archive)); + + return relatedPersons; + } + + private async Task Load() + { + if (_initialized) return; + _initialized = true; + + if (!File.Exists(FilePath)) return; + await using var fileStream = File.OpenRead(FilePath); + using var reader = new BinaryReader(fileStream); + while (reader.BaseStream.Position < reader.BaseStream.Length) + { + var subjectId = reader.ReadInt32(); + var personId = reader.ReadInt32(); + var position = reader.ReadInt16(); + + if (!_mapping.ContainsKey(subjectId)) + _mapping.Add(subjectId, []); + + _mapping[subjectId].Add(new RelatedPerson + { + PersonId = personId, + Position = position + }); + } + } + + private async Task Save() + { + var tempFileName = Path.GetRandomFileName(); + var tempFilePath = Path.Join(archive.TempPath, tempFileName); + await using var outStream = File.OpenWrite(tempFilePath); + await using var writer = new BinaryWriter(outStream); + foreach (var (subjectId, relatedPersons) in _mapping) + foreach (var relatedPerson in relatedPersons) + { + writer.Write(subjectId); + writer.Write(relatedPerson.PersonId); + writer.Write(relatedPerson.Position); + } + + writer.Flush(); + await outStream.FlushAsync(); + outStream.Close(); + + if (File.Exists(FilePath)) + File.Move(FilePath, Path.Join(archive.TempPath, Path.GetRandomFileName()), true); + File.Move(tempFilePath, FilePath, true); + } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/Archive/Relation/SubjectRelations.cs b/Jellyfin.Plugin.Bangumi/Archive/Relation/SubjectRelations.cs new file mode 100644 index 0000000..9cc8b37 --- /dev/null +++ b/Jellyfin.Plugin.Bangumi/Archive/Relation/SubjectRelations.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Plugin.Bangumi.Archive.Data; + +namespace Jellyfin.Plugin.Bangumi.Archive.Relation; + +public class SubjectRelations(ArchiveData archive) +{ + private const string FileName = "subject_relation.map"; + + private readonly Dictionary> _mapping = new(); + + private bool _initialized; + + private string FilePath => Path.Join(archive.BasePath, FileName); + + public async Task GenerateIndex(ZipArchive zipStream, CancellationToken token) + { + var entry = zipStream.GetEntry("subject-relations.jsonlines"); + if (entry == null) return; + await using var stream = entry.Open(); + using var reader = new StreamReader(stream, Encoding.UTF8); + + while (await reader.ReadLineAsync(token) is { } line) + { + var item = JsonSerializer.Deserialize(line, Constants.JsonSerializerOptions); + if (item == null) continue; + if (!_mapping.ContainsKey(item.SubjectId)) + _mapping.Add(item.SubjectId, []); + _mapping[item.SubjectId].Add(item); + } + + await Save(); + } + + public async Task Ready() + { + await Load(); + return _mapping.Count > 0; + } + + public async Task> Get(int subjectId) + { + await Load(); + if (!_mapping.TryGetValue(subjectId, out var rawList)) return []; + + var relatedSubjects = new List(); + foreach (var relatedSubject in rawList) relatedSubjects.Add(await relatedSubject.ToRelatedSubject(archive)); + + return relatedSubjects; + } + + private async Task Load() + { + if (_initialized) return; + _initialized = true; + + if (!File.Exists(FilePath)) return; + await using var fileStream = File.OpenRead(FilePath); + using var reader = new BinaryReader(fileStream); + while (reader.BaseStream.Position < reader.BaseStream.Length) + { + var subjectId = reader.ReadInt32(); + var relatedSubjectId = reader.ReadInt32(); + var relationType = reader.ReadInt16(); + + if (!_mapping.ContainsKey(subjectId)) + _mapping.Add(subjectId, []); + + _mapping[subjectId].Add(new RelatedSubject + { + RelatedSubjectId = relatedSubjectId, + RelationType = relationType + }); + } + } + + private async Task Save() + { + var tempFileName = Path.GetRandomFileName(); + var tempFilePath = Path.Join(archive.TempPath, tempFileName); + await using var outStream = File.OpenWrite(tempFilePath); + await using var writer = new BinaryWriter(outStream); + foreach (var (subjectId, relatedSubjects) in _mapping) + foreach (var relatedSubject in relatedSubjects) + { + writer.Write(subjectId); + writer.Write(relatedSubject.RelatedSubjectId); + writer.Write(relatedSubject.RelationType); + } + + writer.Flush(); + await outStream.FlushAsync(); + outStream.Close(); + + if (File.Exists(FilePath)) + File.Move(FilePath, Path.Join(archive.TempPath, Path.GetRandomFileName()), true); + File.Move(tempFilePath, FilePath, true); + } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/BangumiApi.Jellyfin.cs b/Jellyfin.Plugin.Bangumi/BangumiApi.Jellyfin.cs index 888c0cf..2add0c9 100644 --- a/Jellyfin.Plugin.Bangumi/BangumiApi.Jellyfin.cs +++ b/Jellyfin.Plugin.Bangumi/BangumiApi.Jellyfin.cs @@ -7,12 +7,13 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Plugin.Bangumi.Archive; using Jellyfin.Plugin.Bangumi.OAuth; using MediaBrowser.Common.Net; namespace Jellyfin.Plugin.Bangumi; -public partial class BangumiApi(IHttpClientFactory httpClientFactory, OAuthStore store, Logger logger) +public partial class BangumiApi(IHttpClientFactory httpClientFactory, ArchiveData archive, OAuthStore store, Logger logger) { private readonly Plugin _plugin = Plugin.Instance!; diff --git a/Jellyfin.Plugin.Bangumi/BangumiApi.cs b/Jellyfin.Plugin.Bangumi/BangumiApi.cs index ac35b3b..7a7e4c0 100644 --- a/Jellyfin.Plugin.Bangumi/BangumiApi.cs +++ b/Jellyfin.Plugin.Bangumi/BangumiApi.cs @@ -8,8 +8,6 @@ using System.Threading.Tasks; using Jellyfin.Plugin.Bangumi.Model; using MediaBrowser.Controller.Entities; -#if EMBY -#endif namespace Jellyfin.Plugin.Bangumi; @@ -19,7 +17,9 @@ public partial class BangumiApi private const int Offset = 20; private static string BaseUrl => - string.IsNullOrEmpty(Plugin.Instance?.Configuration?.BaseServerUrl) ? "https://api.bgm.tv" : Plugin.Instance!.Configuration.BaseServerUrl.TrimEnd('/'); + string.IsNullOrEmpty(Plugin.Instance?.Configuration?.BaseServerUrl) + ? "https://api.bgm.tv" + : Plugin.Instance!.Configuration.BaseServerUrl.TrimEnd('/'); public Task> SearchSubject(string keyword, CancellationToken token) { @@ -53,7 +53,8 @@ public async Task> SearchSubject(string keyword, SubjectType? type var tasks = list.Take(num).Select(subject => GetSubject(subject.Id, token)); var subjectWithInfobox = await Task.WhenAll(tasks); - var sortedSubjects = Subject.SortByFuzzScore(subjectWithInfobox.Where(s => s != null).Cast().ToList(), keyword); + var sortedSubjects = + Subject.SortByFuzzScore(subjectWithInfobox.Where(s => s != null).Cast().ToList(), keyword); return sortedSubjects.Concat(list.Skip(num)).ToList(); } @@ -69,6 +70,11 @@ public async Task> SearchSubject(string keyword, SubjectType? type public async Task GetSubject(int id, CancellationToken token) { +#if !EMBY + var subject = await archive.Subject.FindById(id); + if (subject != null) + return subject.ToSubject(); +#endif return await Get($"{BaseUrl}/v0/subjects/{id}", token); } @@ -85,6 +91,14 @@ public async Task> SearchSubject(string keyword, SubjectType? type public async Task?> GetSubjectEpisodeList(int id, EpisodeType? type, double episodeNumber, CancellationToken token) { +#if !EMBY + var episodeList = (await archive.SubjectEpisodeRelation.GetEpisodes(id)) + .Where(x => x.Type == type || type == null) + .Select(x => x.ToEpisode()) + .ToList(); + if (episodeList.Count > 0) return episodeList; +#endif + var result = await GetSubjectEpisodeListWithOffset(id, type, 0, token); if (result == null) return null; @@ -149,6 +163,11 @@ public async Task> SearchSubject(string keyword, SubjectType? type public async Task?> GetSubjectRelations(int id, CancellationToken token) { +#if !EMBY + var relations = await archive.SubjectRelations.Get(id); + if (relations.Count > 0) + return relations; +#endif return await Get>($"{BaseUrl}/v0/subjects/{id}/subjects", token); } @@ -196,13 +215,24 @@ public async Task> GetSubjectCharacters(int id, CancellationTok var characters = await Get>($"{BaseUrl}/v0/subjects/{id}/characters", token); return characters? - .OrderBy(c => c.Relation == "主角" ? 0 : c.Relation == "配角" ? 1 : c.Relation == "客串" ? 2 : 3) + .OrderBy(c => c.Relation switch + { + "主角" => 0, + "配角" => 1, + "客串" => 2, + _ => 3 + }) .SelectMany(character => character.ToPersonInfos()) - .ToList() ?? new List(); + .ToList() ?? []; } public async Task?> GetSubjectPersons(int id, CancellationToken token) { +#if !EMBY + var relatedPerson = await archive.SubjectPersonRelation.Get(id); + if (relatedPerson.Count > 0) + return relatedPerson; +#endif return await Get>($"{BaseUrl}/v0/subjects/{id}/persons", token); } @@ -217,11 +247,33 @@ public async Task> GetSubjectPersonInfos(int id, CancellationTo public async Task GetEpisode(int id, CancellationToken token) { +#if !EMBY + var episode = await archive.Episode.FindById(id); + if (episode != null && DateTime.TryParse(episode.AirDate, out var airDate)) + if (airDate < DateTime.Now.Subtract(TimeSpan.FromDays(7))) + return episode.ToEpisode(); +#endif return await Get($"{BaseUrl}/v0/episodes/{id}", token); } public async Task GetPerson(int id, CancellationToken token) { +#if !EMBY + var person = await archive.Person.FindById(id); + if (person != null) + return person.ToPersonDetail(); +#endif return await Get($"{BaseUrl}/v0/persons/{id}", token); } + + public Task GetPersonImage(int id, CancellationToken token) + { + return GetPersonImage(id, "large", token); + } + + public async Task GetPersonImage(int id, string type, CancellationToken token) + { + var person = await Get($"{BaseUrl}/v0/persons/{id}", token); + return person?.DefaultImage; + } } \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/Model/RelatedPerson.cs b/Jellyfin.Plugin.Bangumi/Model/RelatedPerson.cs index e597769..d366555 100644 --- a/Jellyfin.Plugin.Bangumi/Model/RelatedPerson.cs +++ b/Jellyfin.Plugin.Bangumi/Model/RelatedPerson.cs @@ -38,10 +38,10 @@ public class RelatedPerson public List? Career { get; set; } - public Dictionary Images { get; set; } = new(); + public Dictionary Images { get; set; } = new(); [JsonIgnore] - public string? DefaultImage => Images?["large"]; + public string? DefaultImage => Images.GetValueOrDefault("large", null); public string? Relation { get; set; } diff --git a/Jellyfin.Plugin.Bangumi/Providers/EpisodeProvider.cs b/Jellyfin.Plugin.Bangumi/Providers/EpisodeProvider.cs index c9b2640..45700b8 100644 --- a/Jellyfin.Plugin.Bangumi/Providers/EpisodeProvider.cs +++ b/Jellyfin.Plugin.Bangumi/Providers/EpisodeProvider.cs @@ -6,7 +6,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Plugin.Bangumi.Archive; using Jellyfin.Plugin.Bangumi.Configuration; using Jellyfin.Plugin.Bangumi.Model; using MediaBrowser.Controller.Entities.TV; @@ -17,7 +16,7 @@ namespace Jellyfin.Plugin.Bangumi.Providers; -public partial class EpisodeProvider(BangumiApi api, ArchiveData archive, Logger log, ILibraryManager libraryManager) +public partial class EpisodeProvider(BangumiApi api, Logger log, ILibraryManager libraryManager) : IRemoteMetadataProvider, IHasOrder { private static readonly Regex[] NonEpisodeFileNameRegex = @@ -205,17 +204,7 @@ private static bool IsSpecial(string filePath, bool checkParent = true) { log.Info("fetching episode info using saved id: {EpisodeId}", episodeId); - // search episode in archive - var archivedEpisode = await archive.Episode.FindById(episodeId); - var episode = archivedEpisode?.ToEpisode(); - - // fetch episode from online api if episode was aired recently - if (episode != null && DateTime.TryParse(episode.AirDate, out var airDate)) - if (airDate > DateTime.Now.Subtract(TimeSpan.FromDays(7))) - episode = null; - - // fallback to online api - episode ??= await api.GetEpisode(episodeId, token); + var episode = await api.GetEpisode(episodeId, token); // return if episode still not found if (episode == null) @@ -240,21 +229,8 @@ private static bool IsSpecial(string filePath, bool checkParent = true) } SkipBangumiId: - List? episodeListData = null; - if (await archive.SubjectEpisode.Ready()) - { - log.Info("load subject {SubjectID} episode list from archive", seriesId); - episodeListData = (await archive.SubjectEpisode.GetEpisodes(seriesId)) - .Where(x => x.Type == type || type == null) - .Select(x => x.ToEpisode()) - .ToList(); - } - - if (episodeListData == null) - { - log.Info("searching episode in series episode list"); - episodeListData ??= await api.GetSubjectEpisodeList(seriesId, type, episodeIndex.Value, token); - } + log.Info("searching episode in series episode list"); + var episodeListData = await api.GetSubjectEpisodeList(seriesId, type, episodeIndex.Value, token); if (episodeListData == null) { diff --git a/Jellyfin.Plugin.Bangumi/Providers/PersonImageProvider.cs b/Jellyfin.Plugin.Bangumi/Providers/PersonImageProvider.cs index a034fbf..d3cbe9e 100644 --- a/Jellyfin.Plugin.Bangumi/Providers/PersonImageProvider.cs +++ b/Jellyfin.Plugin.Bangumi/Providers/PersonImageProvider.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -33,23 +32,22 @@ public async Task> GetImages(BaseItem item, Cancell token.ThrowIfCancellationRequested(); if (!int.TryParse(item.GetProviderId(Constants.ProviderName), out var id)) + return []; - return Enumerable.Empty(); + var imageUrl = await api.GetPersonImage(id, token); - var person = await api.GetPerson(id, token); - - if (person != null && person.DefaultImage != "") + if (imageUrl != null) return new List { new() { ProviderName = Constants.PluginName, Type = ImageType.Primary, - Url = person.DefaultImage + Url = imageUrl } }; - return Enumerable.Empty(); + return []; } public async Task GetImageResponse(string url, CancellationToken token) diff --git a/Jellyfin.Plugin.Bangumi/Providers/PersonProvider.cs b/Jellyfin.Plugin.Bangumi/Providers/PersonProvider.cs index 9832cb9..c29e359 100644 --- a/Jellyfin.Plugin.Bangumi/Providers/PersonProvider.cs +++ b/Jellyfin.Plugin.Bangumi/Providers/PersonProvider.cs @@ -3,14 +3,13 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Plugin.Bangumi.Archive; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; namespace Jellyfin.Plugin.Bangumi.Providers; -public class PersonProvider(BangumiApi api, ArchiveData archive) +public class PersonProvider(BangumiApi api) : IRemoteMetadataProvider, IHasOrder { public int Order => -5; @@ -24,12 +23,7 @@ public async Task> GetMetadata(PersonLookupInfo info, Can if (!int.TryParse(info.ProviderIds?.GetValueOrDefault(Constants.ProviderName), out var personId)) return result; - // search person in archive - var archivedPerson = await archive.Person.FindById(personId); - var person = archivedPerson?.ToPersonDetail(); - - // fallback to online api - person ??= await api.GetPerson(personId, token); + var person = await api.GetPerson(personId, token); // return if person still not found if (person == null) diff --git a/Jellyfin.Plugin.Bangumi/Providers/SeasonProvider.cs b/Jellyfin.Plugin.Bangumi/Providers/SeasonProvider.cs index 8efffcc..cff295e 100644 --- a/Jellyfin.Plugin.Bangumi/Providers/SeasonProvider.cs +++ b/Jellyfin.Plugin.Bangumi/Providers/SeasonProvider.cs @@ -5,7 +5,6 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Plugin.Bangumi.Archive; using Jellyfin.Plugin.Bangumi.Configuration; using Jellyfin.Plugin.Bangumi.Model; using MediaBrowser.Controller.Entities.TV; @@ -16,7 +15,7 @@ namespace Jellyfin.Plugin.Bangumi.Providers; -public class SeasonProvider(BangumiApi api, ArchiveData archive, Logger log, ILibraryManager libraryManager) +public class SeasonProvider(BangumiApi api, Logger log, ILibraryManager libraryManager) : IRemoteMetadataProvider, IHasOrder { private static PluginConfiguration Configuration => Plugin.Instance!.Configuration; @@ -81,11 +80,6 @@ public async Task> GetMetadata(SeasonInfo info, Cancellat if (subjectId <= 0) return result; - // search subject in archive - var archivedSubject = await archive.Subject.FindById(subjectId); - subject = archivedSubject?.ToSubject(); - - // fallback to online api subject ??= await api.GetSubject(subjectId, token); // return if subject still not found diff --git a/Jellyfin.Plugin.Bangumi/Providers/SeriesProvider.cs b/Jellyfin.Plugin.Bangumi/Providers/SeriesProvider.cs index 38ca2d8..e73c227 100644 --- a/Jellyfin.Plugin.Bangumi/Providers/SeriesProvider.cs +++ b/Jellyfin.Plugin.Bangumi/Providers/SeriesProvider.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Plugin.Bangumi.Archive; using Jellyfin.Plugin.Bangumi.Configuration; using Jellyfin.Plugin.Bangumi.Model; using MediaBrowser.Controller.Entities.TV; @@ -14,7 +13,7 @@ namespace Jellyfin.Plugin.Bangumi.Providers; -public class SeriesProvider(BangumiApi api, ArchiveData archive, Logger log) +public class SeriesProvider(BangumiApi api, Logger log) : IRemoteMetadataProvider, IHasOrder { private static PluginConfiguration Configuration => Plugin.Instance!.Configuration; @@ -85,12 +84,7 @@ public async Task> GetMetadata(SeriesInfo info, Cancellat if (subjectId == 0) return result; - // search subject in archive - var archivedSubject = await archive.Subject.FindById(subjectId); - var subject = archivedSubject?.ToSubject(); - - // fallback to online api - subject ??= await api.GetSubject(subjectId, token); + var subject = await api.GetSubject(subjectId, token); // return if subject still not found if (subject == null) diff --git a/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs b/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs index 392abb7..a01d0c4 100644 --- a/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs +++ b/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs @@ -85,7 +85,9 @@ public async Task ExecuteAsync(IProgress progress, CancellationToken tok progress.Report(65D + 30D * ++completed / archive.Stores.Count); } - await archive.SubjectEpisode.GenerateIndex(token); + await archive.SubjectRelations.GenerateIndex(zipStream, token); + await archive.SubjectEpisodeRelation.GenerateIndex(token); + await archive.SubjectPersonRelation.GenerateIndex(zipStream, token); log.Info("update completed. cleaning up temp files"); Directory.Delete(archive.TempPath, true);