feat: add tmdb backdrop
This commit is contained in:
parent
194cb3471c
commit
8cb221a204
|
@ -1,13 +1,10 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||||
<PackageReference Include="Moq" Version="4.18.2" />
|
<PackageReference Include="Moq" Version="4.18.2" />
|
||||||
|
@ -15,11 +12,9 @@
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
|
||||||
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Jellyfin.Plugin.MetaShark\Jellyfin.Plugin.MetaShark.csproj" >
|
<ProjectReference Include="..\Jellyfin.Plugin.MetaShark\Jellyfin.Plugin.MetaShark.csproj">
|
||||||
<ExcludeAssets></ExcludeAssets>
|
<ExcludeAssets />
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
</Project>
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
using Jellyfin.Plugin.MetaShark.Api;
|
||||||
|
using Jellyfin.Plugin.MetaShark.Core;
|
||||||
|
using Jellyfin.Plugin.MetaShark.Providers;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Moq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jellyfin.Plugin.MetaShark.Test
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class MovieImageProviderTest
|
||||||
|
{
|
||||||
|
|
||||||
|
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
|
||||||
|
builder.AddSimpleConsole(options =>
|
||||||
|
{
|
||||||
|
options.IncludeScopes = true;
|
||||||
|
options.SingleLine = true;
|
||||||
|
options.TimestampFormat = "hh:mm:ss ";
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestGetMovieImage()
|
||||||
|
{
|
||||||
|
var info = new MediaBrowser.Controller.Entities.Movies.Movie()
|
||||||
|
{
|
||||||
|
Name = "机动战士高达 逆袭的夏亚",
|
||||||
|
PreferredMetadataLanguage = "zh",
|
||||||
|
ProviderIds = new Dictionary<string, string> { { BaseProvider.DoubanProviderId, "1401536" }, { MetadataProvider.Tmdb.ToString(), "16157" } }
|
||||||
|
};
|
||||||
|
var doubanApi = new DoubanApi(loggerFactory);
|
||||||
|
var tmdbApi = new TmdbApi(loggerFactory);
|
||||||
|
var omdbApi = new OmdbApi(loggerFactory);
|
||||||
|
var httpClientFactory = new DefaultHttpClientFactory();
|
||||||
|
var libraryManagerStub = new Mock<ILibraryManager>();
|
||||||
|
var httpContextAccessorStub = new Mock<IHttpContextAccessor>();
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var provider = new MovieImageProvider(httpClientFactory, loggerFactory, libraryManagerStub.Object, httpContextAccessorStub.Object, doubanApi, tmdbApi, omdbApi);
|
||||||
|
var result = await provider.GetImages(info, CancellationToken.None);
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
|
||||||
|
var str = result.ToJson();
|
||||||
|
Console.WriteLine(result.ToJson());
|
||||||
|
}).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,6 +54,14 @@ namespace Jellyfin.Plugin.MetaShark.Test
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.Parse(fileName);
|
||||||
Console.WriteLine(parseResult.ToJson());
|
Console.WriteLine(parseResult.ToJson());
|
||||||
|
|
||||||
|
// 只中文
|
||||||
|
fileName = "机动战士高达 逆袭的夏亚";
|
||||||
|
parseResult = NameParser.Parse(fileName);
|
||||||
|
Console.WriteLine(parseResult.ToJson());
|
||||||
|
|
||||||
|
fileName = "秒速5厘米";
|
||||||
|
parseResult = NameParser.Parse(fileName);
|
||||||
|
Console.WriteLine(parseResult.ToJson());
|
||||||
|
|
||||||
|
|
||||||
// 标题加年份
|
// 标题加年份
|
||||||
|
@ -98,6 +106,9 @@ namespace Jellyfin.Plugin.MetaShark.Test
|
||||||
|
|
||||||
// anime混合中日文
|
// anime混合中日文
|
||||||
fileName = "[异域-11番小队][罗马浴场 THERMAE_ROMAE][1-6+SP][BDRIP][720P][X264-10bit_AAC]";
|
fileName = "[异域-11番小队][罗马浴场 THERMAE_ROMAE][1-6+SP][BDRIP][720P][X264-10bit_AAC]";
|
||||||
|
var anitomyResult = AnitomySharp.AnitomySharp.Parse(fileName);
|
||||||
|
Console.WriteLine(anitomyResult.ToJson());
|
||||||
|
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.Parse(fileName);
|
||||||
Console.WriteLine(parseResult.ToJson());
|
Console.WriteLine(parseResult.ToJson());
|
||||||
|
|
||||||
|
@ -121,9 +132,6 @@ namespace Jellyfin.Plugin.MetaShark.Test
|
||||||
|
|
||||||
// 只英文
|
// 只英文
|
||||||
fileName = "She-Hulk.Attorney.At.Law.S01E01.1080p.WEBRip.x265-RARBG";
|
fileName = "She-Hulk.Attorney.At.Law.S01E01.1080p.WEBRip.x265-RARBG";
|
||||||
var anitomyResult = AnitomySharp.AnitomySharp.Parse(fileName);
|
|
||||||
Console.WriteLine(anitomyResult.ToJson());
|
|
||||||
|
|
||||||
parseResult = NameParser.Parse(fileName);
|
parseResult = NameParser.Parse(fileName);
|
||||||
Console.WriteLine(parseResult.ToJson());
|
Console.WriteLine(parseResult.ToJson());
|
||||||
|
|
||||||
|
|
|
@ -79,8 +79,8 @@ namespace Jellyfin.Plugin.MetaShark.Api
|
||||||
Regex regFamily = new Regex(@"家庭成员: \n(.+?)\n", RegexOptions.Compiled);
|
Regex regFamily = new Regex(@"家庭成员: \n(.+?)\n", RegexOptions.Compiled);
|
||||||
Regex regCelebrityImdb = new Regex(@"imdb编号:\s+?(nm\d+)", RegexOptions.Compiled);
|
Regex regCelebrityImdb = new Regex(@"imdb编号:\s+?(nm\d+)", RegexOptions.Compiled);
|
||||||
|
|
||||||
// 默认1秒请求1次
|
// 默认500毫秒请求1次
|
||||||
private TimeLimiter _defaultTimeConstraint = TimeLimiter.GetFromMaxCountByInterval(1, TimeSpan.FromMilliseconds(1000));
|
private TimeLimiter _defaultTimeConstraint = TimeLimiter.GetFromMaxCountByInterval(1, TimeSpan.FromMilliseconds(500));
|
||||||
// 未登录最多1分钟10次请求,不然5分钟后会被封ip
|
// 未登录最多1分钟10次请求,不然5分钟后会被封ip
|
||||||
private TimeLimiter _guestTimeConstraint = TimeLimiter.Compose(new CountByIntervalAwaitableConstraint(10, TimeSpan.FromMinutes(1)), new CountByIntervalAwaitableConstraint(1, TimeSpan.FromMilliseconds(5000)));
|
private TimeLimiter _guestTimeConstraint = TimeLimiter.Compose(new CountByIntervalAwaitableConstraint(10, TimeSpan.FromMinutes(1)), new CountByIntervalAwaitableConstraint(1, TimeSpan.FromMilliseconds(5000)));
|
||||||
// 登录后最多1分钟20次请求,不然会触发机器人检验
|
// 登录后最多1分钟20次请求,不然会触发机器人检验
|
||||||
|
|
|
@ -35,6 +35,8 @@ public class PluginConfiguration : BasePluginConfiguration
|
||||||
|
|
||||||
public bool EnableTmdbSearch { get; set; } = false;
|
public bool EnableTmdbSearch { get; set; } = false;
|
||||||
|
|
||||||
|
public bool EnableTmdbBackdrop { get; set; } = false;
|
||||||
|
|
||||||
public string TmdbApiKey { get; set; } = string.Empty;
|
public string TmdbApiKey { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string TmdbHost { get; set; } = string.Empty;
|
public string TmdbHost { get; set; } = string.Empty;
|
||||||
|
|
|
@ -69,6 +69,14 @@
|
||||||
</label>
|
</label>
|
||||||
<div class="fieldDescription">勾选后,识别时会同时返回TheMovieDb搜索结果</div>
|
<div class="fieldDescription">勾选后,识别时会同时返回TheMovieDb搜索结果</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
|
<label class="emby-checkbox-label" for="EnableTmdbBackdrop">
|
||||||
|
<input id="EnableTmdbBackdrop" name="EnableTmdbBackdrop" type="checkbox"
|
||||||
|
is="emby-checkbox" />
|
||||||
|
<span>使用TheMovieDb补全背景图</span>
|
||||||
|
</label>
|
||||||
|
<div class="fieldDescription">勾选后,当影片在豆瓣找不到背景图时,改使用TheMovieDb的补全</div>
|
||||||
|
</div>
|
||||||
<div class="inputContainer">
|
<div class="inputContainer">
|
||||||
<label class="inputLabel inputLabelUnfocused" for="TmdbApiKey">Api Key</label>
|
<label class="inputLabel inputLabelUnfocused" for="TmdbApiKey">Api Key</label>
|
||||||
<input id="TmdbApiKey" name="TmdbApiKey" type="text" is="emby-input" />
|
<input id="TmdbApiKey" name="TmdbApiKey" type="text" is="emby-input" />
|
||||||
|
@ -104,6 +112,7 @@
|
||||||
document.querySelector('#EnableDoubanAvoidRiskControl').checked = config.EnableDoubanAvoidRiskControl;
|
document.querySelector('#EnableDoubanAvoidRiskControl').checked = config.EnableDoubanAvoidRiskControl;
|
||||||
document.querySelector('#EnableTmdb').checked = config.EnableTmdb;
|
document.querySelector('#EnableTmdb').checked = config.EnableTmdb;
|
||||||
document.querySelector('#EnableTmdbSearch').checked = config.EnableTmdbSearch;
|
document.querySelector('#EnableTmdbSearch').checked = config.EnableTmdbSearch;
|
||||||
|
document.querySelector('#EnableTmdbBackdrop').checked = config.EnableTmdbBackdrop;
|
||||||
document.querySelector('#TmdbApiKey').value = config.TmdbApiKey;
|
document.querySelector('#TmdbApiKey').value = config.TmdbApiKey;
|
||||||
document.querySelector('#TmdbHost').value = config.TmdbHost;
|
document.querySelector('#TmdbHost').value = config.TmdbHost;
|
||||||
|
|
||||||
|
@ -121,6 +130,7 @@
|
||||||
config.EnableDoubanAvoidRiskControl = document.querySelector('#EnableDoubanAvoidRiskControl').checked;
|
config.EnableDoubanAvoidRiskControl = document.querySelector('#EnableDoubanAvoidRiskControl').checked;
|
||||||
config.EnableTmdb = document.querySelector('#EnableTmdb').checked;
|
config.EnableTmdb = document.querySelector('#EnableTmdb').checked;
|
||||||
config.EnableTmdbSearch = document.querySelector('#EnableTmdbSearch').checked;
|
config.EnableTmdbSearch = document.querySelector('#EnableTmdbSearch').checked;
|
||||||
|
config.EnableTmdbBackdrop = document.querySelector('#EnableTmdbBackdrop').checked;
|
||||||
config.TmdbApiKey = document.querySelector('#TmdbApiKey').value;
|
config.TmdbApiKey = document.querySelector('#TmdbApiKey').value;
|
||||||
config.TmdbHost = document.querySelector('#TmdbHost').value;
|
config.TmdbHost = document.querySelector('#TmdbHost').value;
|
||||||
ApiClient.updatePluginConfiguration(TemplateConfig.pluginUniqueId, config).then(function (result) {
|
ApiClient.updatePluginConfiguration(TemplateConfig.pluginUniqueId, config).then(function (result) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
private static readonly Regex yearReg = new Regex(@"[12][890][0-9][0-9]", RegexOptions.Compiled);
|
private static readonly Regex yearReg = new Regex(@"[12][890][0-9][0-9]", RegexOptions.Compiled);
|
||||||
private static readonly Regex seasonSuffixReg = new Regex(@"[ .]S\d{1,2}$", RegexOptions.Compiled);
|
private static readonly Regex seasonSuffixReg = new Regex(@"[ .]S\d{1,2}$", RegexOptions.Compiled);
|
||||||
|
|
||||||
private static readonly Regex unusedReg = new Regex(@"\[.+?\]|\(.+?\)", RegexOptions.Compiled);
|
private static readonly Regex unusedReg = new Regex(@"\[.+?\]|\(.+?\)|【.+?】", RegexOptions.Compiled);
|
||||||
|
|
||||||
public static ParseNameResult Parse(string fileName, bool isTvSeries = false)
|
public static ParseNameResult Parse(string fileName, bool isTvSeries = false)
|
||||||
{
|
{
|
||||||
|
@ -25,15 +25,16 @@ namespace Jellyfin.Plugin.MetaShark.Core
|
||||||
{
|
{
|
||||||
case AnitomySharp.Element.ElementCategory.ElementAnimeTitle:
|
case AnitomySharp.Element.ElementCategory.ElementAnimeTitle:
|
||||||
// 处理混合中英文的标题,中文一般在最前面,如V字仇杀队.V.for.Vendetta
|
// 处理混合中英文的标题,中文一般在最前面,如V字仇杀队.V.for.Vendetta
|
||||||
char[] seperatorChars = { ' ', '.' };
|
char[] delimiters = { ' ', '.' };
|
||||||
var firstSpaceIndex = item.Value.IndexOfAny(seperatorChars);
|
var firstSpaceIndex = item.Value.IndexOfAny(delimiters);
|
||||||
if (firstSpaceIndex > 0)
|
if (firstSpaceIndex > 0)
|
||||||
{
|
{
|
||||||
var firstString = item.Value.Substring(0, firstSpaceIndex);
|
var firstString = item.Value.Substring(0, firstSpaceIndex);
|
||||||
if (firstString.HasChinese())
|
var lastString = item.Value.Substring(firstSpaceIndex + 1);
|
||||||
|
if (firstString.HasChinese() && !lastString.HasChinese())
|
||||||
{
|
{
|
||||||
parseResult.ChineseName = CleanName(firstString);
|
parseResult.ChineseName = CleanName(firstString);
|
||||||
parseResult.Name = CleanName(item.Value.Substring(firstSpaceIndex + 1));
|
parseResult.Name = CleanName(lastString);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<RemoteImageInfo>();
|
return Enumerable.Empty<RemoteImageInfo>();
|
||||||
}
|
}
|
||||||
var backdropImgs = await GetBackdrop(sid, cancellationToken);
|
var backdropImgs = await GetBackdrop(item, cancellationToken);
|
||||||
|
|
||||||
var res = new List<RemoteImageInfo> {
|
var res = new List<RemoteImageInfo> {
|
||||||
new RemoteImageInfo
|
new RemoteImageInfo
|
||||||
|
@ -139,28 +139,58 @@ namespace Jellyfin.Plugin.MetaShark.Providers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Query for a background photo
|
/// Query for a background photo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sid">a subject/movie id</param>
|
|
||||||
/// <param name="cancellationToken">Instance of the <see cref="CancellationToken"/> interface.</param>
|
/// <param name="cancellationToken">Instance of the <see cref="CancellationToken"/> interface.</param>
|
||||||
private async Task<IEnumerable<RemoteImageInfo>> GetBackdrop(string sid, CancellationToken cancellationToken)
|
private async Task<IEnumerable<RemoteImageInfo>> GetBackdrop(BaseItem item, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
this.Log("GetBackdrop of sid: {0}", sid);
|
var sid = item.GetProviderId(DoubanProviderId);
|
||||||
var photo = await this._doubanApi.GetWallpaperBySidAsync(sid, cancellationToken);
|
var tmdbId = item.GetProviderId(MetadataProvider.Tmdb);
|
||||||
var list = new List<RemoteImageInfo>();
|
var list = new List<RemoteImageInfo>();
|
||||||
|
|
||||||
if (photo == null)
|
// 从豆瓣获取背景图
|
||||||
|
if (!string.IsNullOrEmpty(sid))
|
||||||
|
{
|
||||||
|
var photo = await this._doubanApi.GetWallpaperBySidAsync(sid, cancellationToken);
|
||||||
|
if (photo != null && photo.Count > 0)
|
||||||
|
{
|
||||||
|
this.Log("GetBackdrop from douban sid: {0}", sid);
|
||||||
|
list = photo.Where(x => x.Width > x.Height * 1.3).Select(x =>
|
||||||
|
{
|
||||||
|
return new RemoteImageInfo
|
||||||
|
{
|
||||||
|
ProviderName = Name,
|
||||||
|
Url = x.Large,
|
||||||
|
Type = ImageType.Backdrop,
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (list.Count > 0)
|
||||||
{
|
{
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
return photo.Where(x => x.Width > x.Height * 1.3).Select(x =>
|
// 从TheMovieDb获取背景图
|
||||||
|
if (config.EnableTmdbBackdrop && !string.IsNullOrEmpty(tmdbId))
|
||||||
{
|
{
|
||||||
return new RemoteImageInfo
|
var language = item.GetPreferredMetadataLanguage();
|
||||||
|
var movie = await _tmdbApi
|
||||||
|
.GetMovieAsync(tmdbId.ToInt(), language, language, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (movie != null && !string.IsNullOrEmpty(movie.BackdropPath))
|
||||||
{
|
{
|
||||||
ProviderName = Name,
|
this.Log("GetBackdrop from tmdb id: {0}", tmdbId);
|
||||||
Url = x.Large,
|
list.Add(new RemoteImageInfo
|
||||||
Type = ImageType.Backdrop,
|
{
|
||||||
};
|
ProviderName = Name,
|
||||||
});
|
Url = _tmdbApi.GetBackdropUrl(movie.BackdropPath),
|
||||||
|
Type = ImageType.Backdrop,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue