diff --git a/Jellyfin.Plugin.Bangumi/Archive/ArchiveController.cs b/Jellyfin.Plugin.Bangumi/Archive/ArchiveController.cs new file mode 100644 index 0000000..ee777fb --- /dev/null +++ b/Jellyfin.Plugin.Bangumi/Archive/ArchiveController.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Plugin.Bangumi.Archive; + +[ApiController] +[Route("Plugins/Bangumi/Archive")] +public class OAuthController(ArchiveData archive) + : ControllerBase +{ + [HttpGet("Status")] + [Authorize] + public Dictionary Status() + { + var totalSize = 0L; + DateTime? lastModifyTime = null; + + var directory = new DirectoryInfo(archive.BasePath); + foreach (var info in directory.GetFileSystemInfos("*", SearchOption.AllDirectories)) + { + if (lastModifyTime == null) + lastModifyTime = info.LastWriteTime; + else if (info.LastWriteTime.CompareTo(lastModifyTime) > 0) + lastModifyTime = info.LastWriteTime; + if (info is FileInfo fileInfo) + totalSize += fileInfo.Length; + } + + return new Dictionary + { + ["path"] = archive.BasePath, + ["size"] = totalSize, + ["time"] = lastModifyTime + }; + } + + [HttpDelete("Store")] + [Authorize] + public bool Delete() + { + Directory.Delete(archive.BasePath, true); + Directory.CreateDirectory(archive.BasePath); + return false; + } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/Configuration/ConfigPage.html b/Jellyfin.Plugin.Bangumi/Configuration/ConfigPage.html index f834e48..a893fe4 100644 --- a/Jellyfin.Plugin.Bangumi/Configuration/ConfigPage.html +++ b/Jellyfin.Plugin.Bangumi/Configuration/ConfigPage.html @@ -91,11 +91,28 @@ margin: 0 -16px -16px; backdrop-filter: blur(16px); } + + #bangumiConfigurationForm * ~ .verticalSection { + margin-top: 48px; + } + + #bangumiConfigurationForm .sectionTitleContainer + .fieldDescription { + margin-top: 0; + margin-bottom: 16px; + } + + #bangumi-archive-container { + margin-bottom: 24px; + } + + #bangumi-archive-container:not(.has-archive-data) .archive-data-required { + display: none; + }
-
+

账号授权

@@ -149,7 +166,54 @@

账号授权

-
+
+
+

离线数据库 (Beta)

+ +
+
+ 离线数据库默认每周更新一次,当存在离线数据库时,插件会优先查询离线数据库的数据。
+ 如果需要禁用离线数据库,请关闭计划任务并清理本地文件。 +
+
+
+
+
+
目录
+
-
+
+
+
+
+
大小
+
0
+
+ +
+
+
+
上次更新时间
+
-
+
+ +
+
+
+ +
+ 离线数据库更新后自动更新所有已关联 Bangumi 词条项目的评分(注意可能覆盖其他元数据插件的评分)。 +
+
+

网络

@@ -183,7 +247,7 @@

网络

本插件使用遍历搜索关联条目的方式匹配TV动画季度。大部分情况下只需一次搜索即可完成,但某些条目可能需要多次搜索才能匹配到下一季。默认最多搜索两次,如果某些季度匹配错误,您可以尝试增加最大搜索次数。
-
+

元数据

@@ -237,12 +301,12 @@

元数据

排序时会加入条目别名,提高首个条目匹配率。测试接口未启用此算法
-
+

AnitomySharp

@@ -284,6 +348,53 @@

AnitomySharp

} } + function loadArchiveState() { + return ApiClient.getJSON(ApiClient.getUrl('/Plugins/Bangumi/Archive/Status')).then(function (data) { + // size + var size = data.size || 0; + + if (size > 0) { + container.querySelector('#bangumi-archive-container').classList.add('has-archive-data'); + + var units = ['B', 'KB', 'MB', 'GB', 'TB']; + var index = Math.floor(Math.log2(size) / 10); + container.querySelector('#archive-size').textContent = (size / Math.pow(1024, index)).toFixed(2) + ' ' + units[index]; + } else { + container.querySelector('#bangumi-archive-container').classList.remove('has-archive-data'); + container.querySelector('#archive-folder').textContent = '(不存在)'; + return; + } + + // path + container.querySelector('#archive-folder').textContent = data.path; + + // update time + container.querySelector('#archive-update-time').textContent = data.time ? + new Intl.DateTimeFormat('zh-Hans', { + dateStyle: 'long', + timeStyle: 'long' + }).format(new Date(data.time)) : '-'; + + window.ApiClient.getScheduledTasks().then(function (tasks) { + var task = tasks.find(function (task) { + return task.Key === "ArchiveDataDownloadTask" && task.Category === "Bangumi"; + }); + if (!task) return; + var link = container.querySelector('#archive-update-schedule-link'); + link.style.display = ''; + link.addEventListener('click', function () { + Dashboard.navigate('/dashboard/tasks/edit?id=' + task.Id); + }); + + var button = container.querySelector('#config-archive-update-task'); + button.style.display = ''; + button.addEventListener('click', function () { + Dashboard.navigate('/dashboard/tasks/edit?id=' + task.Id); + }); + }); + }); + } + function loadOAuthState() { return ApiClient.getJSON(ApiClient.getUrl('/Plugins/Bangumi/OAuthState')).then(function (data) { if (!data) { @@ -341,6 +452,7 @@

AnitomySharp

window.addEventListener("message", windowMessageHandler); wrapLoading(Promise.all([ loadConfiguration(), + loadArchiveState(), loadOAuthState(), ])); } @@ -393,6 +505,20 @@

AnitomySharp

container.querySelector('#bangumi-oauth-refresh').style.display = 'none'; })); }); + + container.querySelector('#delete-archive-data').addEventListener('click', function (e) { + e.preventDefault(); + Dashboard.confirm('确定要清空离线数据库吗?', '警告', function (confirmed) { + if (!confirmed) return; + Dashboard.showLoadingMsg(); + wrapLoading(ApiClient.fetch({url: '/Plugins/Bangumi/Archive/Store', type: 'DELETE'}) + .then(function () { + loadArchiveState(); + Dashboard.alert('离线数据库已清空'); + }) + ); + }) + }); })();
diff --git a/Jellyfin.Plugin.Bangumi/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.Bangumi/Configuration/PluginConfiguration.cs index e343313..76ac161 100644 --- a/Jellyfin.Plugin.Bangumi/Configuration/PluginConfiguration.cs +++ b/Jellyfin.Plugin.Bangumi/Configuration/PluginConfiguration.cs @@ -39,4 +39,6 @@ public class PluginConfiguration : BasePluginConfiguration public int SeasonGuessMaxSearchCount { get; set; } = 2; public bool SortByFuzzScore { get; set; } = false; + + public bool RefreshRatingWhenArchiveUpdate { get; set; } = false; } \ No newline at end of file diff --git a/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs b/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs index fc2c0cf..cf72db1 100644 --- a/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs +++ b/Jellyfin.Plugin.Bangumi/ScheduledTask/ArchiveDownloadTask.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Plugin.Bangumi.ScheduledTask; -public class ArchiveDownloadTask(BangumiApi api, ArchiveData archive, ILogger log) : IScheduledTask +public class ArchiveDownloadTask(BangumiApi api, ArchiveData archive, ITaskManager taskManager, ILogger log) : IScheduledTask { private const string ArchiveReleaseUrl = "https://raw.githubusercontent.com/bangumi/Archive/master/aux/latest.json"; private const int StreamCopyBufferSize = 16 * 1024; @@ -110,6 +110,8 @@ public async Task ExecuteAsync(IProgress progress, CancellationToken tok log.LogInformation("update completed. cleaning up temp files"); Directory.Delete(archive.TempPath, true); + + if (Plugin.Instance?.Configuration.RefreshRatingWhenArchiveUpdate == true) taskManager.Execute(); } private async Task GetLatestArchiveMeta(CancellationToken token) diff --git a/Jellyfin.Plugin.Bangumi/ScheduledTask/RatingRefreshTask.cs b/Jellyfin.Plugin.Bangumi/ScheduledTask/RatingRefreshTask.cs index 24ed952..b64366c 100644 --- a/Jellyfin.Plugin.Bangumi/ScheduledTask/RatingRefreshTask.cs +++ b/Jellyfin.Plugin.Bangumi/ScheduledTask/RatingRefreshTask.cs @@ -15,7 +15,7 @@ namespace Jellyfin.Plugin.Bangumi.ScheduledTask; #if EMBY public class RatingRefreshTask(ILibraryManager library, BangumiApi api) #else -public class RatingRefreshTask(ILibraryManager library, ArchiveData archive) +public class RatingRefreshTask(ILibraryManager library, BangumiApi api, ArchiveData archive) #endif : IScheduledTask { @@ -26,18 +26,9 @@ public class RatingRefreshTask(ILibraryManager library, ArchiveData archive) public IEnumerable GetDefaultTriggers() { - return Array.Empty(); + return []; } -#if EMBY - public Task Execute(CancellationToken token, IProgress progress) - { - var task = Task.Run(async () => await ExecuteAsync(progress, token)); - task.Wait(); - return Task.CompletedTask; - } -#endif - public async Task ExecuteAsync(IProgress progress, CancellationToken token) { var idList = library.GetItemIds(new InternalItemsQuery @@ -90,6 +81,7 @@ public async Task ExecuteAsync(IProgress progress, CancellationToken tok #else var archiveSubject = await archive.Subject.FindById(int.Parse(bangumiId!)); var subject = archiveSubject?.ToSubject(); + subject ??= await api.GetSubject(int.Parse(bangumiId!), token); #endif var score = subject?.Rating?.Score; if (score == null) continue; @@ -106,4 +98,11 @@ public async Task ExecuteAsync(IProgress progress, CancellationToken tok #endif } } + + public Task Execute(CancellationToken token, IProgress progress) + { + var task = Task.Run(async () => await ExecuteAsync(progress, token)); + task.Wait(); + return Task.CompletedTask; + } } \ No newline at end of file