Optimize handle danmu

This commit is contained in:
cxfksword
2023-02-20 11:32:05 +08:00
parent 6a55e3708e
commit a1b5e7c3da
8 changed files with 83 additions and 40 deletions

View File

@@ -24,7 +24,7 @@ namespace Jellyfin.Plugin.Danmu.Test
{
var libraryManagerStub = new Mock<ILibraryManager>();
var scraperManager = new ScraperManager(loggerFactory);
scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Mgtv.Mgtv(loggerFactory, libraryManagerStub.Object));
scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Mgtv.Mgtv(loggerFactory));
var fileSystemStub = new Mock<Jellyfin.Plugin.Danmu.Core.IFileSystem>();
var directoryServiceStub = new Mock<IDirectoryService>();
@@ -59,7 +59,7 @@ namespace Jellyfin.Plugin.Danmu.Test
{
var libraryManagerStub = new Mock<ILibraryManager>();
var scraperManager = new ScraperManager(loggerFactory);
scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Mgtv.Mgtv(loggerFactory, libraryManagerStub.Object));
scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Mgtv.Mgtv(loggerFactory));
var fileSystemStub = new Mock<Jellyfin.Plugin.Danmu.Core.IFileSystem>();
var directoryServiceStub = new Mock<IDirectoryService>();
@@ -96,7 +96,7 @@ namespace Jellyfin.Plugin.Danmu.Test
{
var libraryManagerStub = new Mock<ILibraryManager>();
var scraperManager = new ScraperManager(loggerFactory);
scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Mgtv.Mgtv(loggerFactory, libraryManagerStub.Object));
scraperManager.register(new Jellyfin.Plugin.Danmu.Scrapers.Mgtv.Mgtv(loggerFactory));
var fileSystemStub = new Mock<Jellyfin.Plugin.Danmu.Core.IFileSystem>();
var directoryServiceStub = new Mock<IDirectoryService>();
@@ -134,7 +134,7 @@ namespace Jellyfin.Plugin.Danmu.Test
try
{
var libraryManagerStub = new Mock<ILibraryManager>();
var api = new Mgtv(loggerFactory, libraryManagerStub.Object);
var api = new Mgtv(loggerFactory);
var media = await api.GetMedia(new Season(), "514446");
Console.WriteLine(media);
}

View File

@@ -34,8 +34,9 @@ namespace Jellyfin.Plugin.Danmu;
public class LibraryManagerEventsHelper : IDisposable
{
private readonly List<LibraryEvent> _queuedEvents;
private readonly IMemoryCache _pendingAddEventCache;
private readonly MemoryCacheEntryOptions _expiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) };
private readonly IMemoryCache _memoryCache;
private readonly MemoryCacheEntryOptions _pendingAddExpiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) };
private readonly MemoryCacheEntryOptions _danmuUpdatedExpiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) };
private readonly ILibraryManager _libraryManager;
private readonly ILogger<LibraryManagerEventsHelper> _logger;
@@ -62,7 +63,7 @@ public class LibraryManagerEventsHelper : IDisposable
public LibraryManagerEventsHelper(ILibraryManager libraryManager, ILoggerFactory loggerFactory, Jellyfin.Plugin.Danmu.Core.IFileSystem fileSystem, ScraperManager scraperManager)
{
_queuedEvents = new List<LibraryEvent>();
_pendingAddEventCache = new MemoryCache(new MemoryCacheOptions());
_memoryCache = new MemoryCache(new MemoryCacheOptions());
_libraryManager = libraryManager;
_logger = loggerFactory.CreateLogger<LibraryManagerEventsHelper>();
@@ -162,14 +163,14 @@ public class LibraryManagerEventsHelper : IDisposable
{
case Movie when ev.EventType is EventType.Add:
_logger.LogInformation("Movie add: {0}", ev.Item.Name);
_pendingAddEventCache.Set<LibraryEvent>(ev.Item.Id, ev, _expiredOption);
_memoryCache.Set<LibraryEvent>(ev.Item.Id, ev, _pendingAddExpiredOption);
break;
case Movie when ev.EventType is EventType.Update:
_logger.LogInformation("Movie update: {0}", ev.Item.Name);
if (_pendingAddEventCache.TryGetValue<LibraryEvent>(ev.Item.Id, out LibraryEvent addMovieEv))
if (_memoryCache.TryGetValue<LibraryEvent>(ev.Item.Id, out LibraryEvent addMovieEv))
{
queuedMovieAdds.Add(addMovieEv);
_pendingAddEventCache.Remove(ev.Item.Id);
_memoryCache.Remove(ev.Item.Id);
}
else
{
@@ -198,14 +199,14 @@ public class LibraryManagerEventsHelper : IDisposable
break;
case Season when ev.EventType is EventType.Add:
_logger.LogInformation("Season add: {0}", ev.Item.Name);
_pendingAddEventCache.Set<LibraryEvent>(ev.Item.Id, ev, _expiredOption);
_memoryCache.Set<LibraryEvent>(ev.Item.Id, ev, _pendingAddExpiredOption);
break;
case Season when ev.EventType is EventType.Update:
_logger.LogInformation("Season update: {0}", ev.Item.Name);
if (_pendingAddEventCache.TryGetValue<LibraryEvent>(ev.Item.Id, out LibraryEvent addSeasonEv))
if (_memoryCache.TryGetValue<LibraryEvent>(ev.Item.Id, out LibraryEvent addSeasonEv))
{
queuedSeasonAdds.Add(addSeasonEv);
_pendingAddEventCache.Remove(ev.Item.Id);
_memoryCache.Remove(ev.Item.Id);
}
else
{
@@ -466,9 +467,15 @@ public class LibraryManagerEventsHelper : IDisposable
var queueUpdateMeta = new List<BaseItem>();
foreach (var season in seasons)
{
// // 虚拟季第一次请求忽略
// if (season.LocationType == LocationType.Virtual && season.IndexNumber is null)
// {
// continue;
// }
if (season.IndexNumber.HasValue && season.IndexNumber == 0)
{
_logger.LogInformation("特典不处理name={0} number={1}", season.Name, season.IndexNumber);
_logger.LogInformation("special特典文件夹不处理name={0} number={1}", season.Name, season.IndexNumber);
continue;
}
@@ -518,6 +525,12 @@ public class LibraryManagerEventsHelper : IDisposable
{
foreach (var season in seasons)
{
// // 虚拟季第一次请求忽略
// if (season.LocationType == LocationType.Virtual && season.IndexNumber is null)
// {
// continue;
// }
var queueUpdateMeta = new List<BaseItem>();
// GetEpisodes一定要取所有fields要不然更新会导致重建虚拟season季信息
var episodes = season.GetEpisodes(null, new DtoOptions(true));
@@ -526,6 +539,14 @@ public class LibraryManagerEventsHelper : IDisposable
continue;
}
// 不处理季文件夹下的特典和extras影片动画经常会混在一起
var episodesWithoutSP = episodes.Where(x => x.ParentIndexNumber != null && x.ParentIndexNumber > 0).ToList();
if (episodes.Count != episodesWithoutSP.Count)
{
_logger.LogInformation("{0}季存在{1}个特典或extra片段忽略处理.", season.Name, (episodes.Count - episodesWithoutSP.Count));
episodes = episodesWithoutSP;
}
foreach (var scraper in _scraperManager.All())
{
try
@@ -545,16 +566,17 @@ public class LibraryManagerEventsHelper : IDisposable
foreach (var (episode, idx) in episodes.WithIndex())
{
var fileName = Path.GetFileName(episode.Path);
var indexNumber = episode.IndexNumber ?? 0;
if (indexNumber <= 0)
{
_logger.LogInformation("[{0}]匹配失败,缺少集号. [{1}]{2}", scraper.Name, season.Name, episode.Name);
_logger.LogInformation("[{0}]匹配失败,缺少集号. [{1}]{2}", scraper.Name, season.Name, fileName);
continue;
}
if (indexNumber > media.Episodes.Count)
{
_logger.LogInformation("[{0}]匹配失败,集号超过总集数,可能集号错误. [{1}]{2} indexNumber: {3}", scraper.Name, season.Name, episode.Name, indexNumber);
_logger.LogInformation("[{0}]匹配失败,集号超过总集数,可能识别集号错误. [{1}]{2} indexNumber: {3}", scraper.Name, season.Name, fileName, indexNumber);
continue;
}
@@ -697,10 +719,20 @@ public class LibraryManagerEventsHelper : IDisposable
var episodeList = season.GetEpisodes(null, new DtoOptions(true));
foreach (var (episode, idx) in episodeList.WithIndex())
{
var fileName = Path.GetFileName(episode.Path);
// 没对应剧集号的,忽略处理
var indexNumber = episode.IndexNumber ?? 0;
if (indexNumber < 1 || indexNumber > media.Episodes.Count)
{
_logger.LogInformation("[{0}]缺少集号或集号超过弹幕数,忽略处理. [{1}]{2}", scraper.Name, season.Name, fileName);
continue;
}
// 特典或extras影片不处理动画经常会放在季文件夹下
if (episode.ParentIndexNumber is null or 0)
{
_logger.LogInformation("[{0}]缺少季号可能是特典或extras影片忽略处理. [{1}]{2}", scraper.Name, season.Name, fileName);
continue;
}
@@ -757,17 +789,30 @@ public class LibraryManagerEventsHelper : IDisposable
try
{
// 弹幕5分钟内更新过忽略处理有时Update事件会重复执行
if (!ignoreCheck && IsRepeatAction(item))
var checkDownloadedKey = $"{item.Id}_{commentId}";
if (!ignoreCheck && _memoryCache.TryGetValue(checkDownloadedKey, out var latestDownloaded))
{
_logger.LogInformation("[{0}]最近5分钟已更新过弹幕xml忽略处理{1}", scraper.Name, item.Name);
_logger.LogInformation("[{0}]最近5分钟已更新过弹幕xml忽略处理{1}.{2}", scraper.Name, item.IndexNumber, item.Name);
return;
}
_memoryCache.Set(checkDownloadedKey, true, _danmuUpdatedExpiredOption);
var danmaku = await scraper.GetDanmuContent(item, commentId);
if (danmaku != null)
{
await this.DownloadDanmuInternal(item, danmaku.ToXml());
this._logger.LogInformation("[{0}]弹幕下载成功name={1}", scraper.Name, item.Name);
var bytes = danmaku.ToXml();
if (bytes.Length < 10 * 1024)
{
_memoryCache.Remove(checkDownloadedKey);
_logger.LogInformation("[{0}]弹幕内容少于10KB忽略处理{1}.{2}", scraper.Name, item.IndexNumber, item.Name);
return;
}
await this.SaveDanmu(item, bytes);
this._logger.LogInformation("[{0}]弹幕下载成功name={1}.{2} commentId={3}", scraper.Name, item.IndexNumber, item.Name, commentId);
}
else
{
_memoryCache.Remove(checkDownloadedKey);
}
}
catch (Exception ex)
@@ -776,11 +821,12 @@ public class LibraryManagerEventsHelper : IDisposable
}
}
private bool IsRepeatAction(BaseItem item)
private bool IsRepeatAction(BaseItem item, string checkDownloadedKey)
{
// 单元测试时为null
if (item.FileNameWithoutExtension == null) return false;
// 通过xml文件属性判断多线程时判断有误
var danmuPath = Path.Combine(item.ContainingFolderPath, item.FileNameWithoutExtension + ".xml");
if (!this._fileSystem.Exists(danmuPath))
{
@@ -792,7 +838,7 @@ public class LibraryManagerEventsHelper : IDisposable
return diff.TotalSeconds < 300;
}
private async Task DownloadDanmuInternal(BaseItem item, byte[] bytes)
private async Task SaveDanmu(BaseItem item, byte[] bytes)
{
// 单元测试时为null
if (item.FileNameWithoutExtension == null) return;

View File

@@ -302,7 +302,7 @@ public class Bilibili : AbstractScraper
var score = searchName.Distance(title);
if (score < 0.7)
{
log.LogInformation("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
log.LogDebug("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
continue;
}
@@ -310,7 +310,7 @@ public class Bilibili : AbstractScraper
var itemPubYear = item.ProductionYear ?? 0;
if (itemPubYear > 0 && pubYear > 0 && itemPubYear != pubYear)
{
log.LogInformation("[{0}] 发行年份不一致,忽略处理. b站{1} jellyfin: {2}", title, pubYear, itemPubYear);
log.LogDebug("[{0}] 发行年份不一致,忽略处理. b站{1} jellyfin: {2}", title, pubYear, itemPubYear);
continue;
}

View File

@@ -97,7 +97,7 @@ public class Dandan : AbstractScraper
var score = searchName.Distance(title);
if (score < 0.7)
{
log.LogInformation("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
log.LogDebug("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
continue;
}
@@ -105,7 +105,7 @@ public class Dandan : AbstractScraper
var itemPubYear = item.ProductionYear ?? 0;
if (itemPubYear > 0 && pubYear > 0 && itemPubYear != pubYear)
{
log.LogInformation("[{0}] 发行年份不一致,忽略处理. dandan{1} jellyfin: {2}", title, pubYear, itemPubYear);
log.LogDebug("[{0}] 发行年份不一致,忽略处理. dandan{1} jellyfin: {2}", title, pubYear, itemPubYear);
continue;
}

View File

@@ -100,7 +100,7 @@ public class Iqiyi : AbstractScraper
var score = searchName.Distance(title);
if (score < 0.7)
{
log.LogInformation("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
log.LogDebug("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
continue;
}
@@ -108,7 +108,7 @@ public class Iqiyi : AbstractScraper
var itemPubYear = item.ProductionYear ?? 0;
if (itemPubYear > 0 && pubYear > 0 && itemPubYear != pubYear)
{
log.LogInformation("[{0}] 发行年份不一致,忽略处理. Iqiyi{1} jellyfin: {2}", title, pubYear, itemPubYear);
log.LogDebug("[{0}] 发行年份不一致,忽略处理. Iqiyi{1} jellyfin: {2}", title, pubYear, itemPubYear);
continue;
}

View File

@@ -24,13 +24,11 @@ public class Mgtv : AbstractScraper
public const string ScraperProviderId = "MgtvID";
private readonly MgtvApi _api;
private readonly ILibraryManager _libraryManager;
public Mgtv(ILoggerFactory logManager, ILibraryManager libraryManager)
public Mgtv(ILoggerFactory logManager)
: base(logManager.CreateLogger<Mgtv>())
{
_api = new MgtvApi(logManager);
_libraryManager = libraryManager;
}
public override int DefaultOrder => 6;
@@ -112,7 +110,7 @@ public class Mgtv : AbstractScraper
var score = searchName.Distance(title);
if (score < 0.7)
{
log.LogInformation("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
log.LogDebug("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
continue;
}
@@ -120,7 +118,7 @@ public class Mgtv : AbstractScraper
var itemPubYear = item.ProductionYear ?? 0;
if (itemPubYear > 0 && pubYear > 0 && itemPubYear != pubYear)
{
log.LogInformation("[{0}] 发行年份不一致,忽略处理. year: {1} jellyfin: {2}", title, pubYear, itemPubYear);
log.LogDebug("[{0}] 发行年份不一致,忽略处理. year: {1} jellyfin: {2}", title, pubYear, itemPubYear);
continue;
}
@@ -181,9 +179,8 @@ public class Mgtv : AbstractScraper
// 从季信息元数据中获取cid值
// 没SXX季文件夹时GetParent是Series有时GetParent是Season所以需要通过seasonId中获取
var seasonId = ((MediaBrowser.Controller.Entities.TV.Episode)item).FindSeasonId();
var season = _libraryManager.GetItemById(seasonId);
// 不能通过GetParent获取Season因为没有SXX季文件夹时GetParent是Series
var season = ((MediaBrowser.Controller.Entities.TV.Episode)item).Season;
season.ProviderIds.TryGetValue(ScraperProviderId, out var cid);
return new ScraperEpisode() { Id = id, CommentId = $"{cid},{id}" };
}

View File

@@ -107,7 +107,7 @@ public class Tencent : AbstractScraper
var score = searchName.Distance(title);
if (score < 0.7)
{
log.LogInformation("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
log.LogDebug("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
continue;
}
@@ -115,7 +115,7 @@ public class Tencent : AbstractScraper
var itemPubYear = item.ProductionYear ?? 0;
if (itemPubYear > 0 && pubYear > 0 && itemPubYear != pubYear)
{
log.LogInformation("[{0}] 发行年份不一致,忽略处理. year: {1} jellyfin: {2}", title, pubYear, itemPubYear);
log.LogDebug("[{0}] 发行年份不一致,忽略处理. year: {1} jellyfin: {2}", title, pubYear, itemPubYear);
continue;
}

View File

@@ -107,7 +107,7 @@ public class Youku : AbstractScraper
var score = searchName.Distance(title);
if (score < 0.7)
{
log.LogInformation("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
log.LogDebug("[{0}] 标题差异太大,忽略处理. 搜索词:{1}, score: {2}", title, searchName, score);
continue;
}
@@ -115,7 +115,7 @@ public class Youku : AbstractScraper
var itemPubYear = item.ProductionYear ?? 0;
if (itemPubYear > 0 && pubYear > 0 && itemPubYear != pubYear)
{
log.LogInformation("[{0}] 发行年份不一致,忽略处理. Youku{1} jellyfin: {2}", title, pubYear, itemPubYear);
log.LogDebug("[{0}] 发行年份不一致,忽略处理. Youku{1} jellyfin: {2}", title, pubYear, itemPubYear);
continue;
}