mirror of
https://github.com/cxfksword/jellyfin-plugin-danmu.git
synced 2026-02-02 17:59:58 +08:00
feat: add danmu api scraper. close #102
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Plugin.Danmu.Configuration;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
|
||||
namespace Jellyfin.Plugin.Danmu.Test
|
||||
{
|
||||
@@ -19,15 +26,60 @@ namespace Jellyfin.Plugin.Danmu.Test
|
||||
options.TimestampFormat = "hh:mm:ss ";
|
||||
}));
|
||||
|
||||
protected Plugin? mockPlugin;
|
||||
protected PluginConfiguration mockConfiguration = new PluginConfiguration();
|
||||
|
||||
[TestInitialize]
|
||||
public void SetUp()
|
||||
{
|
||||
DotNetEnv.Env.TraversePath().Load();
|
||||
|
||||
// Mock Plugin.Instance 及其依赖项
|
||||
var mockApplicationPaths = new Mock<IApplicationPaths>();
|
||||
mockApplicationPaths.Setup(p => p.PluginConfigurationsPath).Returns(Path.GetTempPath());
|
||||
mockApplicationPaths.Setup(p => p.PluginsPath).Returns(Path.GetTempPath());
|
||||
|
||||
var mockApplicationHost = new Mock<IApplicationHost>();
|
||||
mockApplicationHost.Setup(h => h.GetExports<Scrapers.AbstractScraper>(false))
|
||||
.Returns(new List<Scrapers.AbstractScraper>());
|
||||
|
||||
var mockXmlSerializer = new Mock<IXmlSerializer>();
|
||||
|
||||
var mockScraperManager = new Mock<Scrapers.ScraperManager>(
|
||||
Mock.Of<ILoggerFactory>());
|
||||
|
||||
try
|
||||
{
|
||||
// 创建 Plugin 实例
|
||||
mockPlugin = new Plugin(
|
||||
mockApplicationPaths.Object,
|
||||
mockApplicationHost.Object,
|
||||
mockXmlSerializer.Object,
|
||||
mockScraperManager.Object
|
||||
);
|
||||
|
||||
// 使用反射设置 Configuration
|
||||
var configField = typeof(Plugin).BaseType?
|
||||
.GetField("_configuration", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (configField != null)
|
||||
{
|
||||
configField.SetValue(mockPlugin, mockConfiguration);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果无法创建 Plugin 实例,使用反射直接设置静态实例
|
||||
// 这是一个后备方案
|
||||
}
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void TearDown()
|
||||
{
|
||||
// 清理 Plugin.Instance
|
||||
var instanceProperty = typeof(Plugin).GetProperty("Instance", BindingFlags.Public | BindingFlags.Static);
|
||||
instanceProperty?.SetValue(null, null);
|
||||
|
||||
// 清理代码
|
||||
// 例如,释放资源或重置状态
|
||||
}
|
||||
|
||||
94
Jellyfin.Plugin.Danmu.Test/DanmuApiApiTest.cs
Normal file
94
Jellyfin.Plugin.Danmu.Test/DanmuApiApiTest.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Plugin.Danmu.Scrapers.DanmuApi;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Jellyfin.Plugin.Danmu.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class DanmuApiApiTest : BaseTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestSearchAsync()
|
||||
{
|
||||
var keyword = "火影忍者";
|
||||
var api = new DanmuApiApi(loggerFactory);
|
||||
|
||||
// 注意:此测试需要实际的服务器 URL,否则会返回空结果
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await api.SearchAsync(keyword, CancellationToken.None);
|
||||
Console.WriteLine($"Search results count: {result.Count}");
|
||||
foreach (var anime in result)
|
||||
{
|
||||
Console.WriteLine($" - {anime.AnimeTitle} (ID: {anime.BangumiId})");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestGetBangumiAsync()
|
||||
{
|
||||
var bangumiId = "mzc00200nc1cbum";
|
||||
var api = new DanmuApiApi(loggerFactory);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await api.GetBangumiAsync(bangumiId, CancellationToken.None);
|
||||
if (result != null)
|
||||
{
|
||||
Console.WriteLine($"Bangumi: {result.AnimeTitle}");
|
||||
Console.WriteLine($"Episodes count: {result.Episodes.Count}");
|
||||
foreach (var episode in result.Episodes.Take(5))
|
||||
{
|
||||
Console.WriteLine($" - {episode.EpisodeId}: {episode.EpisodeTitle}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Bangumi not found or server not configured");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestGetCommentsAsync()
|
||||
{
|
||||
var commentId = "14435";
|
||||
var api = new DanmuApiApi(loggerFactory);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await api.GetCommentsAsync(commentId, CancellationToken.None);
|
||||
Console.WriteLine($"Comments count: {result.Count}");
|
||||
foreach (var comment in result.Take(5))
|
||||
{
|
||||
Console.WriteLine($" - {comment.M} (CID: {comment.Cid})");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
265
Jellyfin.Plugin.Danmu.Test/DanmuApiTest.cs
Normal file
265
Jellyfin.Plugin.Danmu.Test/DanmuApiTest.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Plugin.Danmu.Configuration;
|
||||
using Jellyfin.Plugin.Danmu.Model;
|
||||
using Jellyfin.Plugin.Danmu.Scrapers;
|
||||
using Jellyfin.Plugin.Danmu.Scrapers.DanmuApi;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
namespace Jellyfin.Plugin.Danmu.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class DanmuApiTest : BaseTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestSearchMovie()
|
||||
{
|
||||
var scraper = new DanmuApi(loggerFactory);
|
||||
var item = new Movie
|
||||
{
|
||||
Name = "火影忍者"
|
||||
};
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await scraper.Search(item);
|
||||
Console.WriteLine($"Search results count: {result.Count}");
|
||||
foreach (var searchInfo in result)
|
||||
{
|
||||
Console.WriteLine($" - {searchInfo.Name} (ID: {searchInfo.Id}, Episodes: {searchInfo.EpisodeSize})");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestSearchMediaId()
|
||||
{
|
||||
var scraper = new DanmuApi(loggerFactory);
|
||||
var item = new Movie
|
||||
{
|
||||
Name = "阿丽塔:战斗天使(2019)【外语电影】from youku",
|
||||
ProductionYear = 2019
|
||||
};
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var mediaId = await scraper.SearchMediaId(item);
|
||||
if (mediaId != null)
|
||||
{
|
||||
Console.WriteLine($"Found media ID: {mediaId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Media ID not found or server not configured");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestGetMedia()
|
||||
{
|
||||
var scraper = new DanmuApi(loggerFactory);
|
||||
var item = new Movie
|
||||
{
|
||||
Name = "测试电影"
|
||||
};
|
||||
var testBangumiId = "test-bangumi-id";
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var media = await scraper.GetMedia(item, testBangumiId);
|
||||
if (media != null)
|
||||
{
|
||||
Console.WriteLine($"Media ID: {media.Id}");
|
||||
Console.WriteLine($"Comment ID: {media.CommentId}");
|
||||
Console.WriteLine($"Episodes count: {media.Episodes.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Media not found or server not configured");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestGetDanmuContent()
|
||||
{
|
||||
var scraper = new DanmuApi(loggerFactory);
|
||||
var item = new Movie
|
||||
{
|
||||
Name = "测试电影"
|
||||
};
|
||||
var commentId = "test-comment-id";
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var danmaku = await scraper.GetDanmuContent(item, commentId);
|
||||
if (danmaku != null)
|
||||
{
|
||||
Console.WriteLine($"Chat Server: {danmaku.ChatServer}");
|
||||
Console.WriteLine($"Danmaku items count: {danmaku.Items.Count}");
|
||||
foreach (var item in danmaku.Items.Take(5))
|
||||
{
|
||||
Console.WriteLine($" - [{item.Progress}ms] {item.Content}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Danmaku not found or server not configured");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFilterByAllowedSources()
|
||||
{
|
||||
// 模拟配置只允许特定采集源
|
||||
var originalConfig = Plugin.Instance?.Configuration?.DanmuApi?.AllowedSources;
|
||||
|
||||
try
|
||||
{
|
||||
if (Plugin.Instance?.Configuration?.DanmuApi != null)
|
||||
{
|
||||
Plugin.Instance.Configuration.DanmuApi.AllowedSources = "bilibili";
|
||||
}
|
||||
|
||||
var scraper = new DanmuApi(loggerFactory);
|
||||
var item = new Movie
|
||||
{
|
||||
Name = "火影忍者"
|
||||
};
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await scraper.Search(item);
|
||||
Console.WriteLine($"Source filter test - Results count: {result.Count}");
|
||||
|
||||
// 验证所有结果都来自允许的采集源
|
||||
foreach (var searchInfo in result)
|
||||
{
|
||||
Console.WriteLine($" - {searchInfo.Name} (ID: {searchInfo.Id})");
|
||||
}
|
||||
|
||||
if (result.Count > 0)
|
||||
{
|
||||
Console.WriteLine("✓ Source filter is working - only allowed sources returned");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("⚠ No results found with source filter");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 恢复原始配置
|
||||
if (Plugin.Instance?.Configuration?.DanmuApi != null)
|
||||
{
|
||||
Plugin.Instance.Configuration.DanmuApi.AllowedSources = originalConfig ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFilterByAllowedPlatforms()
|
||||
{
|
||||
// 模拟配置只允许特定平台
|
||||
var originalConfig = Plugin.Instance?.Configuration?.DanmuApi?.AllowedPlatforms;
|
||||
|
||||
try
|
||||
{
|
||||
if (Plugin.Instance?.Configuration?.DanmuApi != null)
|
||||
{
|
||||
Plugin.Instance.Configuration.DanmuApi.AllowedPlatforms = "qq";
|
||||
}
|
||||
|
||||
var scraper = new DanmuApi(loggerFactory);
|
||||
var item = new Movie
|
||||
{
|
||||
Name = "又见逍遥"
|
||||
};
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await scraper.GetMedia(item, "5510");
|
||||
Console.WriteLine($"Platform filter test - Results count: {result.Episodes.Count}");
|
||||
|
||||
// 验证所有结果都来自允许的平台
|
||||
foreach (var episode in result.Episodes)
|
||||
{
|
||||
Console.WriteLine($" - {episode.Title} (ID: {episode.Id})");
|
||||
}
|
||||
|
||||
if (result.Episodes.Count > 0)
|
||||
{
|
||||
Console.WriteLine("✓ Platform filter is working - only allowed platforms returned");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("⚠ No results found with platform filter");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 恢复原始配置
|
||||
if (Plugin.Instance?.Configuration?.DanmuApi != null)
|
||||
{
|
||||
Plugin.Instance.Configuration.DanmuApi.AllowedPlatforms = originalConfig ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user