From 2e7b3e56fe7743a4c683b4fe9acb44f754912c26 Mon Sep 17 00:00:00 2001 From: cxfksword <718792+cxfksword@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:28:56 +0800 Subject: [PATCH] Support like subtitle plugin --- Jellyfin.Plugin.Danmu.Test/BilibiliTest.cs | 132 +++++++++++++ .../Core/Extensions/JsonExtension.cs | 17 ++ .../Core/Extensions/StringExtension.cs | 6 + .../DanmuSubtitleProvider.cs | 184 ++++++++++++++++++ .../LibraryManagerEventsHelper.cs | 150 +++++++++++++- Jellyfin.Plugin.Danmu/Model/EventType.cs | 7 +- Jellyfin.Plugin.Danmu/Model/SubtitleId.cs | 18 ++ Jellyfin.Plugin.Danmu/PluginStartup.cs | 20 +- .../ScheduledTasks/RefreshDanmakuTask.cs | 19 +- .../ScheduledTasks/ScanLibraryTask.cs | 6 + .../Scrapers/AbstractScraper.cs | 10 +- .../Scrapers/Bilibili/Bilibili.cs | 40 ++++ .../Scrapers/Bilibili/BilibiliApi.cs | 66 ++++--- .../Scrapers/Bilibili/Entity/Media.cs | 2 + .../Scrapers/Dandan/Dandan.cs | 34 ++++ .../Scrapers/Entity/ScraperSearchInfo.cs | 11 ++ Jellyfin.Plugin.Danmu/Scrapers/Iqiyi/Iqiyi.cs | 39 +++- Jellyfin.Plugin.Danmu/Scrapers/Youku/Youku.cs | 38 +++- README.md | 28 ++- doc/tutorial.png | Bin 0 -> 79033 bytes 20 files changed, 751 insertions(+), 76 deletions(-) create mode 100644 Jellyfin.Plugin.Danmu.Test/BilibiliTest.cs create mode 100644 Jellyfin.Plugin.Danmu/DanmuSubtitleProvider.cs create mode 100644 Jellyfin.Plugin.Danmu/Model/SubtitleId.cs create mode 100644 Jellyfin.Plugin.Danmu/Scrapers/Entity/ScraperSearchInfo.cs create mode 100644 doc/tutorial.png diff --git a/Jellyfin.Plugin.Danmu.Test/BilibiliTest.cs b/Jellyfin.Plugin.Danmu.Test/BilibiliTest.cs new file mode 100644 index 0000000..378cff0 --- /dev/null +++ b/Jellyfin.Plugin.Danmu.Test/BilibiliTest.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Jellyfin.Plugin.Danmu.Model; +using Jellyfin.Plugin.Danmu.Scrapers; +using Jellyfin.Plugin.Danmu.Scrapers.Bilibili; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using Microsoft.Extensions.Logging; +using Moq; + +namespace Jellyfin.Plugin.Danmu.Test +{ + + [TestClass] + public class BilibiliTest + { + ILoggerFactory loggerFactory = LoggerFactory.Create(builder => + builder.AddSimpleConsole(options => + { + options.IncludeScopes = true; + options.SingleLine = true; + options.TimestampFormat = "hh:mm:ss "; + })); + + [TestMethod] + public void TestSearch() + { + var scraperManager = new ScraperManager(loggerFactory); + scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Bilibili.Bilibili(loggerFactory)); + + var fileSystemStub = new Mock(); + var directoryServiceStub = new Mock(); + var libraryManagerStub = new Mock(); + var libraryManagerEventsHelper = new LibraryManagerEventsHelper(libraryManagerStub.Object, loggerFactory, fileSystemStub.Object, scraperManager); + + var item = new Movie + { + Name = "扬名立万" + }; + + Task.Run(async () => + { + try + { + var scraper = new Bilibili(loggerFactory); + var result = await scraper.Search(item); + Console.WriteLine(result); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + }).GetAwaiter().GetResult(); + + } + + + [TestMethod] + public void TestAddMovie() + { + var scraperManager = new ScraperManager(loggerFactory); + scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Bilibili.Bilibili(loggerFactory)); + + var fileSystemStub = new Mock(); + var directoryServiceStub = new Mock(); + var libraryManagerStub = new Mock(); + var libraryManagerEventsHelper = new LibraryManagerEventsHelper(libraryManagerStub.Object, loggerFactory, fileSystemStub.Object, scraperManager); + + var item = new Movie + { + Name = "异邦人:无皇刃谭" + }; + + var list = new List(); + list.Add(new LibraryEvent { Item = item, EventType = EventType.Add }); + + Task.Run(async () => + { + try + { + await libraryManagerEventsHelper.ProcessQueuedMovieEvents(list, EventType.Add); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + }).GetAwaiter().GetResult(); + + } + + + [TestMethod] + public void TestUpdateMovie() + { + var scraperManager = new ScraperManager(loggerFactory); + scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Bilibili.Bilibili(loggerFactory)); + + var fileSystemStub = new Mock(); + var directoryServiceStub = new Mock(); + var libraryManagerStub = new Mock(); + var libraryManagerEventsHelper = new LibraryManagerEventsHelper(libraryManagerStub.Object, loggerFactory, fileSystemStub.Object, scraperManager); + + var item = new Movie + { + Name = "异邦人:无皇刃谭", + ProviderIds = new Dictionary() { { Bilibili.ScraperProviderId, "2185" } }, + }; + + var list = new List(); + list.Add(new LibraryEvent { Item = item, EventType = EventType.Update }); + + Task.Run(async () => + { + try + { + await libraryManagerEventsHelper.ProcessQueuedMovieEvents(list, EventType.Update); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + }).GetAwaiter().GetResult(); + + } + + } +} diff --git a/Jellyfin.Plugin.Danmu/Core/Extensions/JsonExtension.cs b/Jellyfin.Plugin.Danmu/Core/Extensions/JsonExtension.cs index 159fcd6..12ae540 100644 --- a/Jellyfin.Plugin.Danmu/Core/Extensions/JsonExtension.cs +++ b/Jellyfin.Plugin.Danmu/Core/Extensions/JsonExtension.cs @@ -18,5 +18,22 @@ namespace Jellyfin.Plugin.Danmu.Core.Extensions jso.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping; return JsonSerializer.Serialize(obj, jso); } + + public static T? FromJson(this string str) + { + if (string.IsNullOrEmpty(str)) return default(T?); + + // 不指定UnsafeRelaxedJsonEscaping,+号会被转码为unicode字符,和js/java的序列化不一致 + var jso = new JsonSerializerOptions(); + jso.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping; + try + { + return JsonSerializer.Deserialize(str, jso); + } + catch (Exception ex) + { + return default(T?); + } + } } } diff --git a/Jellyfin.Plugin.Danmu/Core/Extensions/StringExtension.cs b/Jellyfin.Plugin.Danmu/Core/Extensions/StringExtension.cs index af4fc78..0ac1fec 100644 --- a/Jellyfin.Plugin.Danmu/Core/Extensions/StringExtension.cs +++ b/Jellyfin.Plugin.Danmu/Core/Extensions/StringExtension.cs @@ -80,6 +80,12 @@ namespace Jellyfin.Plugin.Danmu.Core.Extensions } } + public static string ToBase64(this string str) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(str); + return System.Convert.ToBase64String(plainTextBytes); + } + public static double Distance(this string s1, string s2) { var jw = new JaroWinkler(); diff --git a/Jellyfin.Plugin.Danmu/DanmuSubtitleProvider.cs b/Jellyfin.Plugin.Danmu/DanmuSubtitleProvider.cs new file mode 100644 index 0000000..61e7db9 --- /dev/null +++ b/Jellyfin.Plugin.Danmu/DanmuSubtitleProvider.cs @@ -0,0 +1,184 @@ +using System.Linq; +using System.Net.Mime; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Plugin.Danmu.Core.Extensions; +using Jellyfin.Plugin.Danmu.Scrapers; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; +using Jellyfin.Plugin.Danmu.Model; +using System.Text.Json; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Dto; + +namespace Jellyfin.Plugin.Danmu; + +public class DanmuSubtitleProvider : ISubtitleProvider +{ + public string Name => "Danmu"; + + private readonly ILibraryManager _libraryManager; + private readonly ILogger _logger; + private readonly LibraryManagerEventsHelper _libraryManagerEventsHelper; + + private readonly ScraperManager _scraperManager; + + public IEnumerable SupportedMediaTypes => new List() { VideoContentType.Movie, VideoContentType.Episode }; + + public DanmuSubtitleProvider(ILibraryManager libraryManager, ILoggerFactory loggerFactory, ScraperManager scraperManager, LibraryManagerEventsHelper libraryManagerEventsHelper) + { + _libraryManager = libraryManager; + _logger = loggerFactory.CreateLogger(); + _scraperManager = scraperManager; + _libraryManagerEventsHelper = libraryManagerEventsHelper; + } + + public async Task GetSubtitles(string id, CancellationToken cancellationToken) + { + var base64EncodedBytes = System.Convert.FromBase64String(id); + id = System.Text.Encoding.UTF8.GetString(base64EncodedBytes); + var info = id.FromJson(); + if (info == null) + { + throw new ArgumentException(); + } + + var item = _libraryManager.GetItemById(info.ItemId); + if (item == null) + { + throw new ArgumentException(); + } + + var scraper = _scraperManager.All().FirstOrDefault(x => x.ProviderId == info.ProviderId); + if (scraper != null) + { + UpdateDanmuMetadata(item, scraper.ProviderId, info.Id); + _libraryManagerEventsHelper.QueueItem(item, EventType.Force); + + // if (item is Movie) + // { + // var media = await scraper.GetMedia(item, info.Id); + // if (media != null) + // { + // await ForceSaveProviderId(item, scraper.ProviderId, media.Id); + // } + // } + + // if (item is Episode) + // { + // var season = ((Episode)item).Season; + // if (season != null) + // { + // var media = await scraper.GetMedia(season, info.Id); + // if (media != null) + // { + + // // 更新季元数据 + // await ForceSaveProviderId(season, scraper.ProviderId, media.Id); + + // // 更新所有剧集元数据,GetEpisodes一定要取所有fields,要不然更新会导致重建虚拟season季信息 + // var episodes = season.GetEpisodes(null, new DtoOptions(true)); + // foreach (var (episode, idx) in episodes.WithIndex()) + // { + // // 没对应剧集号的,忽略处理 + // var indexNumber = episode.IndexNumber ?? 0; + // if (indexNumber < 1 || indexNumber > media.Episodes.Count) + // { + // continue; + // } + + // var epId = media.Episodes[indexNumber - 1].Id; + // await ForceSaveProviderId(episode, scraper.ProviderId, epId); + // } + + // } + // } + + // } + } + + throw new Exception($"弹幕下载已由{Plugin.Instance?.Name}插件接管,请忽略本异常."); + } + + public async Task> Search(SubtitleSearchRequest request, CancellationToken cancellationToken) + { + var list = new List(); + if (request.IsAutomated || string.IsNullOrEmpty(request.MediaPath)) + { + return list; + } + + var item = _libraryManager.GetItemList(new InternalItemsQuery + { + Path = request.MediaPath, + }).FirstOrDefault(); + + if (item == null) + { + return list; + } + + // 剧集使用series名称进行搜索 + if (item is Episode) + { + item.Name = request.SeriesName; + } + + foreach (var scraper in _scraperManager.All()) + { + try + { + + var result = await scraper.Search(item); + foreach (var searchInfo in result) + { + var title = searchInfo.Name; + if (!string.IsNullOrEmpty(searchInfo.Category)) + { + title = $"[{searchInfo.Category}] {searchInfo.Name}"; + } + if (searchInfo.Year != null) + { + title += $" ({searchInfo.Year})"; + } + var idInfo = new SubtitleId() { ItemId = item.Id.ToString(), Id = searchInfo.Id.ToString(), ProviderId = scraper.ProviderId }; + list.Add(new RemoteSubtitleInfo() + { + Id = idInfo.ToJson().ToBase64(), // Id不允许特殊字幕,做base64编码处理 + Name = title, + ProviderName = $"{Name}", + Format = "xml", + Comment = $"来源:{scraper.Name}", + }); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "[{0}]Exception handled processing queued movie events", scraper.Name); + } + } + + + return list; + } + + private void UpdateDanmuMetadata(BaseItem item, string providerId, string providerVal) + { + // 先清空旧弹幕的所有元数据 + foreach (var s in _scraperManager.All()) + { + item.ProviderIds.Remove(s.ProviderId); + } + // 保存指定弹幕元数据 + item.ProviderIds[providerId] = providerVal; + } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Danmu/LibraryManagerEventsHelper.cs b/Jellyfin.Plugin.Danmu/LibraryManagerEventsHelper.cs index 5b43640..56fb9c1 100644 --- a/Jellyfin.Plugin.Danmu/LibraryManagerEventsHelper.cs +++ b/Jellyfin.Plugin.Danmu/LibraryManagerEventsHelper.cs @@ -138,8 +138,10 @@ public class LibraryManagerEventsHelper : IDisposable var queuedMovieAdds = new List(); var queuedMovieUpdates = new List(); + var queuedMovieForces = new List(); var queuedEpisodeAdds = new List(); - var queuedEpisodeUpdates = new List(); ; + var queuedEpisodeUpdates = new List(); + var queuedEpisodeForces = new List(); var queuedShowAdds = new List(); var queuedShowUpdates = new List(); var queuedSeasonAdds = new List(); @@ -148,6 +150,14 @@ public class LibraryManagerEventsHelper : IDisposable // add事件可能会在获取元数据完之前执行,导致可能会中断元数据获取,通过pending集合把add事件延缓到获取元数据后再执行(获取完元数据后,一般会多推送一个update事件) foreach (var ev in queue) { + + // item所在的媒体库不启用弹幕插件,忽略处理 + if (IsIgnoreItem(ev.Item)) + { + continue; + } + + switch (ev.Item) { case Movie when ev.EventType is EventType.Add: @@ -166,6 +176,10 @@ public class LibraryManagerEventsHelper : IDisposable queuedMovieUpdates.Add(ev); } break; + case Movie when ev.EventType is EventType.Force: + _logger.LogInformation("Movie force: {0}", ev.Item.Name); + queuedMovieForces.Add(ev); + break; case Series when ev.EventType is EventType.Add: _logger.LogInformation("Series add: {0}", ev.Item.Name); // _pendingAddEventCache.Set(ev.Item.Id, ev, _expiredOption); @@ -202,6 +216,10 @@ public class LibraryManagerEventsHelper : IDisposable _logger.LogInformation("Episode update: {0}", ev.Item.Name); queuedEpisodeUpdates.Add(ev); break; + case Episode when ev.EventType is EventType.Force: + _logger.LogInformation("Episode force: {0}", ev.Item.Name); + queuedEpisodeForces.Add(ev); + break; } } @@ -217,6 +235,22 @@ public class LibraryManagerEventsHelper : IDisposable await ProcessQueuedShowEvents(queuedShowUpdates, EventType.Update).ConfigureAwait(false); await ProcessQueuedSeasonEvents(queuedSeasonUpdates, EventType.Update).ConfigureAwait(false); await ProcessQueuedEpisodeEvents(queuedEpisodeUpdates, EventType.Update).ConfigureAwait(false); + + await ProcessQueuedMovieEvents(queuedMovieForces, EventType.Force).ConfigureAwait(false); + await ProcessQueuedEpisodeEvents(queuedEpisodeForces, EventType.Force).ConfigureAwait(false); + } + + public bool IsIgnoreItem(BaseItem item) + { + // item所在的媒体库不启用弹幕插件,忽略处理 + var libraryOptions = _libraryManager.GetLibraryOptions(item); + if (libraryOptions == null || libraryOptions.DisabledSubtitleFetchers.Contains(Plugin.Instance?.Name)) + { + // Console.WriteLine($"ignore {item.Path} {item.Name}"); + return true; + } + + return false; } @@ -327,7 +361,39 @@ public class LibraryManagerEventsHelper : IDisposable } } + // 强制刷新指定来源弹幕 + if (eventType == EventType.Force) + { + foreach (var item in movies) + { + // 找到强制的scraper + var scraper = _scraperManager.All().FirstOrDefault(x => item.ProviderIds.ContainsKey(x.ProviderId)); + if (scraper == null) + { + continue; + } + var mediaId = item.GetProviderId(scraper.ProviderId); + if (string.IsNullOrEmpty(mediaId)) + { + continue; + } + + + var media = await scraper.GetMedia(item, mediaId); + if (media != null) + { + var episode = await scraper.GetMediaEpisode(item, media.Id); + if (episode != null) + { + // 下载弹幕xml文件 + await this.DownloadDanmu(scraper, item, episode.CommentId, true).ConfigureAwait(false); + } + + await this.ForceSaveProviderId(item, scraper.ProviderId, media.Id); + } + } + } } @@ -450,7 +516,8 @@ public class LibraryManagerEventsHelper : IDisposable foreach (var season in seasons) { var queueUpdateMeta = new List(); - var episodes = season.GetEpisodes(null, new DtoOptions(false)); + // GetEpisodes一定要取所有fields,要不然更新会导致重建虚拟season季信息 + var episodes = season.GetEpisodes(null, new DtoOptions(true)); if (episodes == null) { continue; @@ -587,6 +654,63 @@ public class LibraryManagerEventsHelper : IDisposable } } + + + // 强制刷新指定来源弹幕 + if (eventType == EventType.Force) + { + foreach (var item in episodes) + { + // 找到强制的scraper + var scraper = _scraperManager.All().FirstOrDefault(x => item.ProviderIds.ContainsKey(x.ProviderId)); + if (scraper == null) + { + continue; + } + + var mediaId = item.GetProviderId(scraper.ProviderId); + if (string.IsNullOrEmpty(mediaId)) + { + continue; + } + + + var season = ((Episode)item).Season; + if (season == null) + { + continue; + } + + var media = await scraper.GetMedia(season, mediaId); + if (media != null) + { + // 更新所有剧集元数据,GetEpisodes一定要取所有fields,要不然更新会导致重建虚拟season季信息 + var episodeList = season.GetEpisodes(null, new DtoOptions(true)); + foreach (var (episode, idx) in episodeList.WithIndex()) + { + // 没对应剧集号的,忽略处理 + var indexNumber = episode.IndexNumber ?? 0; + if (indexNumber < 1 || indexNumber > media.Episodes.Count) + { + continue; + } + + var epId = media.Episodes[indexNumber - 1].Id; + var commentId = media.Episodes[indexNumber - 1].CommentId; + + // 下载弹幕xml文件 + await this.DownloadDanmu(scraper, episode, commentId, true).ConfigureAwait(false); + + // 更新剧集元数据 + await ForceSaveProviderId(episode, scraper.ProviderId, epId); + } + + // 更新季元数据 + await ForceSaveProviderId(season, scraper.ProviderId, media.Id); + + } + } + } } @@ -607,6 +731,11 @@ public class LibraryManagerEventsHelper : IDisposable // 合并新添加的provider id foreach (var pair in queueItem.ProviderIds) { + if (string.IsNullOrEmpty(pair.Value)) + { + continue; + } + item.ProviderIds[pair.Key] = pair.Value; } @@ -617,13 +746,13 @@ public class LibraryManagerEventsHelper : IDisposable _logger.LogInformation("更新epid到元数据完成。item数:{0}", queue.Count); } - public async Task DownloadDanmu(AbstractScraper scraper, BaseItem item, string commentId) + public async Task DownloadDanmu(AbstractScraper scraper, BaseItem item, string commentId, bool ignoreCheck = false) { // 下载弹幕xml文件 try { // 弹幕5分钟内更新过,忽略处理(有时Update事件会重复执行) - if (IsRepeatAction(item)) + if (!ignoreCheck && IsRepeatAction(item)) { _logger.LogInformation("[{0}]最近5分钟已更新过弹幕xml,忽略处理:{1}", scraper.Name, item.Name); return; @@ -697,6 +826,19 @@ public class LibraryManagerEventsHelper : IDisposable } } + private async Task ForceSaveProviderId(BaseItem item, string providerId, string providerVal) + { + // 先清空旧弹幕的所有元数据 + foreach (var s in _scraperManager.All()) + { + item.ProviderIds.Remove(s.ProviderId); + } + // 保存指定弹幕元数据 + item.ProviderIds[providerId] = providerVal; + + await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + } + public void Dispose() { diff --git a/Jellyfin.Plugin.Danmu/Model/EventType.cs b/Jellyfin.Plugin.Danmu/Model/EventType.cs index 2560cb9..187239d 100644 --- a/Jellyfin.Plugin.Danmu/Model/EventType.cs +++ b/Jellyfin.Plugin.Danmu/Model/EventType.cs @@ -24,5 +24,10 @@ public enum EventType /// /// The update event. /// - Update + Update, + + /// + /// The force update event. + /// + Force } diff --git a/Jellyfin.Plugin.Danmu/Model/SubtitleId.cs b/Jellyfin.Plugin.Danmu/Model/SubtitleId.cs new file mode 100644 index 0000000..deaf164 --- /dev/null +++ b/Jellyfin.Plugin.Danmu/Model/SubtitleId.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; + +namespace Jellyfin.Plugin.Danmu.Model; + +public class SubtitleId +{ + public string ItemId { get; set; } + + public string Id { get; set; } + + public string ProviderId { get; set; } +} diff --git a/Jellyfin.Plugin.Danmu/PluginStartup.cs b/Jellyfin.Plugin.Danmu/PluginStartup.cs index dbfde1c..b963cfb 100644 --- a/Jellyfin.Plugin.Danmu/PluginStartup.cs +++ b/Jellyfin.Plugin.Danmu/PluginStartup.cs @@ -95,6 +95,7 @@ namespace Jellyfin.Plugin.Danmu return; } + // 当剧集没有SXX/Season XX季文件夹时,LocationType就是Virtual,动画经常没有季文件夹 if (itemChangeEventArgs.Item.LocationType == LocationType.Virtual && itemChangeEventArgs.Item is not Season) { return; @@ -103,25 +104,6 @@ namespace Jellyfin.Plugin.Danmu _libraryManagerEventsHelper.QueueItem(itemChangeEventArgs.Item, EventType.Update); } - /// - /// Library item was removed. - /// - /// The sending entity. - /// The . - private void LibraryManagerItemRemoved(object sender, ItemChangeEventArgs itemChangeEventArgs) - { - if (itemChangeEventArgs.Item is not Movie and not Episode and not Series and not Season) - { - return; - } - - if (itemChangeEventArgs.Item.LocationType == LocationType.Virtual) - { - return; - } - - _libraryManagerEventsHelper.QueueItem(itemChangeEventArgs.Item, EventType.Remove); - } /// public void Dispose() diff --git a/Jellyfin.Plugin.Danmu/ScheduledTasks/RefreshDanmakuTask.cs b/Jellyfin.Plugin.Danmu/ScheduledTasks/RefreshDanmakuTask.cs index 9cb9878..45aa830 100644 --- a/Jellyfin.Plugin.Danmu/ScheduledTasks/RefreshDanmakuTask.cs +++ b/Jellyfin.Plugin.Danmu/ScheduledTasks/RefreshDanmakuTask.cs @@ -52,12 +52,13 @@ namespace Jellyfin.Plugin.Danmu.ScheduledTasks public IEnumerable GetDefaultTriggers() { - yield return new TaskTriggerInfo - { - Type = TaskTriggerInfo.TriggerWeekly, - DayOfWeek = DayOfWeek.Monday, - TimeOfDayTicks = TimeSpan.FromHours(4).Ticks - }; + return new List(); + // yield return new TaskTriggerInfo + // { + // Type = TaskTriggerInfo.TriggerWeekly, + // DayOfWeek = DayOfWeek.Monday, + // TimeOfDayTicks = TimeSpan.FromHours(4).Ticks + // }; } public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) @@ -92,6 +93,12 @@ namespace Jellyfin.Plugin.Danmu.ScheduledTasks continue; } + // item所在的媒体库不启用弹幕插件,忽略处理 + if (_libraryManagerEventsHelper.IsIgnoreItem(item)) + { + continue; + } + // 推送下载最新的xml (season刷新会同时刷新episode,所以不需要再推送episode,而且season是bv号的,只能通过season来刷新) switch (item) diff --git a/Jellyfin.Plugin.Danmu/ScheduledTasks/ScanLibraryTask.cs b/Jellyfin.Plugin.Danmu/ScheduledTasks/ScanLibraryTask.cs index 85c8bc6..93911d9 100644 --- a/Jellyfin.Plugin.Danmu/ScheduledTasks/ScanLibraryTask.cs +++ b/Jellyfin.Plugin.Danmu/ScheduledTasks/ScanLibraryTask.cs @@ -87,6 +87,12 @@ namespace Jellyfin.Plugin.Danmu.ScheduledTasks continue; } + // item所在的媒体库不启用弹幕插件,忽略处理 + if (_libraryManagerEventsHelper.IsIgnoreItem(item)) + { + continue; + } + // 推送刷新 (season刷新会同时刷新episode,所以不需要再推送episode,而且season是bv号的,只能通过season来刷新) switch (item) { diff --git a/Jellyfin.Plugin.Danmu/Scrapers/AbstractScraper.cs b/Jellyfin.Plugin.Danmu/Scrapers/AbstractScraper.cs index fc1bea9..827f5f9 100644 --- a/Jellyfin.Plugin.Danmu/Scrapers/AbstractScraper.cs +++ b/Jellyfin.Plugin.Danmu/Scrapers/AbstractScraper.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Jellyfin.Plugin.Danmu.Scrapers.Entity; using MediaBrowser.Controller.Entities; @@ -32,7 +33,14 @@ public abstract class AbstractScraper } /// - /// 搜索影片id + /// 搜索影片 + /// + /// 元数据item + /// 影片列表 + public abstract Task> Search(BaseItem item); + + /// + /// 搜索匹配的影片id /// /// 元数据item /// 影片id diff --git a/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Bilibili.cs b/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Bilibili.cs index a6d5d6e..88d52c0 100644 --- a/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Bilibili.cs +++ b/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Bilibili.cs @@ -36,6 +36,46 @@ public class Bilibili : AbstractScraper public override string ProviderId => ScraperProviderId; + public override async Task> Search(BaseItem item) + { + var list = new List(); + var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; + var searchName = this.NormalizeSearchName(item.Name); + try + { + var searchResult = await _api.SearchAsync(searchName, CancellationToken.None).ConfigureAwait(false); + if (searchResult != null && searchResult.Result != null) + { + foreach (var result in searchResult.Result) + { + if ((result.ResultType == "media_ft" || result.ResultType == "media_bangumi") && result.Data.Length > 0) + { + foreach (var media in result.Data) + { + var seasonId = media.SeasonId; + var title = media.Title; + var pubYear = Jellyfin.Plugin.Danmu.Core.Utils.UnixTimeStampToDateTime(media.PublishTime).Year; + + list.Add(new ScraperSearchInfo() + { + Id = $"{seasonId}", + Name = title, + Category = media.SeasonTypeName, + Year = pubYear, + }); + } + } + } + } + } + catch (Exception ex) + { + log.LogError(ex, "Exception handled GetMatchSeasonId. {0}", searchName); + } + + return list; + } + public override async Task SearchMediaId(BaseItem item) { var searchName = this.NormalizeSearchName(item.Name); diff --git a/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/BilibiliApi.cs b/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/BilibiliApi.cs index ad09e73..ed379a9 100644 --- a/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/BilibiliApi.cs +++ b/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/BilibiliApi.cs @@ -52,6 +52,40 @@ public class BilibiliApi : IDisposable _memoryCache = new MemoryCache(new MemoryCacheOptions()); } + + public async Task SearchAsync(string keyword, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(keyword)) + { + return new SearchResult(); + } + + var cacheKey = $"search_{keyword}"; + var expiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) }; + SearchResult searchResult; + if (_memoryCache.TryGetValue(cacheKey, out searchResult)) + { + return searchResult; + } + + await this.LimitRequestFrequently(); + await EnsureSessionCookie(cancellationToken).ConfigureAwait(false); + + keyword = HttpUtility.UrlEncode(keyword); + var url = $"http://api.bilibili.com/x/web-interface/search/all/v2?keyword={keyword}"; + var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + var result = await response.Content.ReadFromJsonAsync>(_jsonOptions, cancellationToken).ConfigureAwait(false); + if (result != null && result.Code == 0 && result.Data != null) + { + _memoryCache.Set(cacheKey, result.Data, expiredOption); + return result.Data; + } + + _memoryCache.Set(cacheKey, new SearchResult(), expiredOption); + return new SearchResult(); + } + /// /// Get bilibili danmu data. /// @@ -136,38 +170,6 @@ public class BilibiliApi : IDisposable } - public async Task SearchAsync(string keyword, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(keyword)) - { - return new SearchResult(); - } - - var cacheKey = $"search_{keyword}"; - var expiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) }; - SearchResult searchResult; - if (_memoryCache.TryGetValue(cacheKey, out searchResult)) - { - return searchResult; - } - - await this.LimitRequestFrequently(); - await EnsureSessionCookie(cancellationToken).ConfigureAwait(false); - - keyword = HttpUtility.UrlEncode(keyword); - var url = $"http://api.bilibili.com/x/web-interface/search/all/v2?keyword={keyword}"; - var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); - response.EnsureSuccessStatusCode(); - var result = await response.Content.ReadFromJsonAsync>(_jsonOptions, cancellationToken).ConfigureAwait(false); - if (result != null && result.Code == 0 && result.Data != null) - { - _memoryCache.Set(cacheKey, result.Data, expiredOption); - return result.Data; - } - - _memoryCache.Set(cacheKey, new SearchResult(), expiredOption); - return new SearchResult(); - } public async Task GetSeasonAsync(long seasonId, CancellationToken cancellationToken) { diff --git a/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Entity/Media.cs b/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Entity/Media.cs index 4353f94..50e8320 100644 --- a/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Entity/Media.cs +++ b/Jellyfin.Plugin.Danmu/Scrapers/Bilibili/Entity/Media.cs @@ -20,6 +20,8 @@ namespace Jellyfin.Plugin.Danmu.Scrapers.Bilibili.Entity [JsonPropertyName("season_type")] public int SeasonType { get; set; } + [JsonPropertyName("season_type_name")] + public string SeasonTypeName { get; set; } [JsonPropertyName("season_id")] public long SeasonId { get; set; } diff --git a/Jellyfin.Plugin.Danmu/Scrapers/Dandan/Dandan.cs b/Jellyfin.Plugin.Danmu/Scrapers/Dandan/Dandan.cs index a3d90b3..de3884f 100644 --- a/Jellyfin.Plugin.Danmu/Scrapers/Dandan/Dandan.cs +++ b/Jellyfin.Plugin.Danmu/Scrapers/Dandan/Dandan.cs @@ -37,6 +37,40 @@ public class Dandan : AbstractScraper public override string ProviderId => ScraperProviderId; + public override async Task> Search(BaseItem item) + { + var list = new List(); + var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; + var searchName = this.NormalizeSearchName(item.Name); + var animes = await this._api.SearchAsync(searchName, CancellationToken.None).ConfigureAwait(false); + foreach (var anime in animes) + { + var animeId = anime.AnimeId; + var title = anime.AnimeTitle; + var pubYear = anime.Year; + + if (isMovieItemType && anime.Type != "movie") + { + continue; + } + + if (!isMovieItemType && anime.Type == "movie") + { + continue; + } + + list.Add(new ScraperSearchInfo() + { + Id = $"{animeId}", + Name = title, + Category = anime.Type == "movie" ? "电影" : "电视剧", + Year = pubYear, + }); + } + + return list; + } + public override async Task SearchMediaId(BaseItem item) { var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; diff --git a/Jellyfin.Plugin.Danmu/Scrapers/Entity/ScraperSearchInfo.cs b/Jellyfin.Plugin.Danmu/Scrapers/Entity/ScraperSearchInfo.cs new file mode 100644 index 0000000..3de6ca4 --- /dev/null +++ b/Jellyfin.Plugin.Danmu/Scrapers/Entity/ScraperSearchInfo.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Jellyfin.Plugin.Danmu.Scrapers.Entity; + +public class ScraperSearchInfo +{ + public string Id { get; set; } + public string Name { get; set; } + public string Category { get; set; } = string.Empty; + public int? Year { get; set; } +} \ No newline at end of file diff --git a/Jellyfin.Plugin.Danmu/Scrapers/Iqiyi/Iqiyi.cs b/Jellyfin.Plugin.Danmu/Scrapers/Iqiyi/Iqiyi.cs index 1ca9f02..c4b9b90 100644 --- a/Jellyfin.Plugin.Danmu/Scrapers/Iqiyi/Iqiyi.cs +++ b/Jellyfin.Plugin.Danmu/Scrapers/Iqiyi/Iqiyi.cs @@ -39,8 +39,44 @@ public class Iqiyi : AbstractScraper public override string ProviderId => ScraperProviderId; - public override async Task SearchMediaId(BaseItem item) + public override async Task> Search(BaseItem item) { + var list = new List(); + var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; + var searchName = this.NormalizeSearchName(item.Name); + var videos = await this._api.GetSuggestAsync(searchName, CancellationToken.None).ConfigureAwait(false); + foreach (var video in videos) + { + var videoId = video.VideoId; + var title = video.Name; + var pubYear = video.Year; + + if (isMovieItemType && video.ChannelName != "电影") + { + continue; + } + + if (!isMovieItemType && video.ChannelName == "电影") + { + continue; + } + + list.Add(new ScraperSearchInfo() + { + Id = $"{video.LinkId}", + Name = title, + Category = video.ChannelName, + Year = pubYear, + }); + } + + + return list; + } + + public override async Task SearchMediaId(BaseItem item) + { + var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; var searchName = this.NormalizeSearchName(item.Name); var videos = await this._api.GetSuggestAsync(searchName, CancellationToken.None).ConfigureAwait(false); foreach (var video in videos) @@ -48,7 +84,6 @@ public class Iqiyi : AbstractScraper var videoId = video.VideoId; var title = video.Name; var pubYear = video.Year; - var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; if (isMovieItemType && video.ChannelName != "电影") { diff --git a/Jellyfin.Plugin.Danmu/Scrapers/Youku/Youku.cs b/Jellyfin.Plugin.Danmu/Scrapers/Youku/Youku.cs index 99f8d37..26acf88 100644 --- a/Jellyfin.Plugin.Danmu/Scrapers/Youku/Youku.cs +++ b/Jellyfin.Plugin.Danmu/Scrapers/Youku/Youku.cs @@ -39,8 +39,43 @@ public class Youku : AbstractScraper public override string ProviderId => ScraperProviderId; - public override async Task SearchMediaId(BaseItem item) + public override async Task> Search(BaseItem item) { + var list = new List(); + var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; + var searchName = this.NormalizeSearchName(item.Name); + var videos = await this._api.SearchAsync(searchName, CancellationToken.None).ConfigureAwait(false); + foreach (var video in videos) + { + var videoId = video.ID; + var title = video.Title; + var pubYear = video.Year; + + if (isMovieItemType && video.Type != "movie") + { + continue; + } + + if (!isMovieItemType && video.Type == "movie") + { + continue; + } + + list.Add(new ScraperSearchInfo() + { + Id = $"{videoId}", + Name = title, + Category = video.Type == "movie" ? "电影" : "电视剧", + Year = pubYear, + }); + } + + return list; + } + + public override async Task SearchMediaId(BaseItem item) + { + var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; var searchName = this.NormalizeSearchName(item.Name); var videos = await this._api.SearchAsync(searchName, CancellationToken.None).ConfigureAwait(false); foreach (var video in videos) @@ -48,7 +83,6 @@ public class Youku : AbstractScraper var videoId = video.ID; var title = video.Title; var pubYear = video.Year; - var isMovieItemType = item is MediaBrowser.Controller.Entities.Movies.Movie; if (isMovieItemType && video.Type != "movie") { diff --git a/README.md b/README.md index 3b83af5..0bfd1e6 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ jellyfin弹幕自动下载插件,已支持的弹幕来源:b站,弹弹play * 自动下载xml格式弹幕 * 生成ass格式弹幕 -* 定时更新 +* 可配置定时更新 * 支持api访问弹幕 ![logo](doc/logo.png) @@ -27,10 +27,16 @@ jellyfin弹幕自动下载插件,已支持的弹幕来源:b站,弹弹play ## 如何使用 -* 新加入的影片会自动获取弹幕(只匹配番剧和电影视频),旧影片可以通过计划任务**扫描媒体库匹配弹幕**手动执行获取 -* 可以在元数据中手动指定匹配的视频ID,如播放链接`https://www.bilibili.com/bangumi/play/ep682965`,对应的视频ID就是`682965` -* 对于电视剧和动画,可以在元数据中指定季ID,如播放链接`https://www.bilibili.com/bangumi/play/ss1564`,对应的季ID就是`1564`,只要集数和b站的集数的一致,并正确填写了集号,每季视频的弹幕会自动获取 -* 同时生成ass弹幕,需要在插件配置中打开,默认是关闭的 +1. 安装后,进入`控制台 -> 插件`,查看下`Danmu`插件是否是**Active**状态 +2. 进入`控制台 -> 媒体库`,点击任一媒体库进入配置页,在最下面的`字幕下载`选项中勾选**Danmu**,并保存 + + + +3. 新加入的影片会自动获取弹幕(只匹配番剧和电影视频),旧影片可以通过计划任务**扫描媒体库匹配弹幕**手动执行获取 +4. 可以在元数据中手动指定匹配的视频ID,如播放链接`https://www.bilibili.com/bangumi/play/ep682965`,对应的视频ID就是`682965` +5. 对于电视剧和动画,可以在元数据中指定季ID,如播放链接`https://www.bilibili.com/bangumi/play/ss1564`,对应的季ID就是`1564`,只要集数和b站的集数的一致,并正确填写了集号,每季视频的弹幕会自动获取 +6. 同时生成ass弹幕,需要在插件配置中打开,默认是关闭的 +7. 定时更新需要自己到计划任务中添加定时时间,默认手工执行更新 > 电影或季元数据也支持手动指定BV号,来匹配UP主上传的视频弹幕。多P视频和剧集是按顺序一一对应匹配的,所以保证jellyfin中剧集有正确的集号很重要 @@ -65,8 +71,12 @@ ass格式: 3. Build plugin with following command. ```sh -$ dotnet restore -$ dotnet publish Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj +dotnet restore +dotnet publish --output=artifacts Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj + +# remove unused dll +cd artifacts +rm -rf MediaBrowser*.dll Microsoft*.dll Newtonsoft*.dll System*.dll Emby*.dll Jellyfin.Data*.dll Jellyfin.Extensions*.dll *.json *.pdb ``` @@ -74,9 +84,9 @@ $ dotnet publish Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj 1. Build the plugin -2. Create a folder, like `Danmu` and copy `bin/Release/Jellyfin.Plugin.Danmu.dll` into it +2. Create a folder, like `danmu` and copy `artifacts/*.dll` into it -3. Move folder `Danmu` to jellyfin `data/plugins` folder +3. Move folder `danmu` to jellyfin `data/plugins` folder ## Thanks diff --git a/doc/tutorial.png b/doc/tutorial.png new file mode 100644 index 0000000000000000000000000000000000000000..2d62f3bc83684e4b8470b7ff69c4ed343e05e5db GIT binary patch literal 79033 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