Fix actor image error #5

This commit is contained in:
cxfksword 2022-12-18 12:01:25 +08:00
parent ff7286af98
commit 66bafb9199
13 changed files with 328 additions and 28 deletions

View File

@ -60,7 +60,7 @@ namespace Jellyfin.Plugin.MetaShark.Test
[TestMethod]
public void TestGetVideoByBvidAsync()
public void TestGetVideoBySidAsync()
{
var sid = "26654184";
@ -80,6 +80,28 @@ namespace Jellyfin.Plugin.MetaShark.Test
}).GetAwaiter().GetResult();
}
[TestMethod]
public void TestFixGetImage()
{
// 演员入驻了豆瓣, 下载的不是演员的头像#5
var sid = "35460157";
var api = new DoubanApi(loggerFactory);
Task.Run(async () =>
{
try
{
var result = await api.GetMovieAsync(sid, CancellationToken.None);
Assert.AreEqual<string>("https://img2.doubanio.com/view/celebrity/raw/public/p1598199472.61.jpg", result.Celebrities.First(x => x.Name == "刘陆").Img);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}).GetAwaiter().GetResult();
}
[TestMethod]
public void TestGetCelebritiesBySidAsync()
{
@ -100,5 +122,26 @@ namespace Jellyfin.Plugin.MetaShark.Test
}
}).GetAwaiter().GetResult();
}
[TestMethod]
public void TestGetCelebritiesByCidAsync()
{
var sid = "1340364";
var api = new DoubanApi(loggerFactory);
Task.Run(async () =>
{
try
{
var result = await api.GetCelebrityAsync(sid, CancellationToken.None);
TestContext.WriteLine(result.ToJson());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}).GetAwaiter().GetResult();
}
}
}

View File

@ -3,6 +3,7 @@ using Jellyfin.Plugin.MetaShark.Core;
using Jellyfin.Plugin.MetaShark.Providers;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
using Moq;
using System;
@ -69,5 +70,26 @@ namespace Jellyfin.Plugin.MetaShark.Test
}).GetAwaiter().GetResult();
}
[TestMethod]
public void TestGetMetadataByTMDB()
{
var info = new MovieInfo() { Name = "人生大事", MetadataLanguage = "zh", ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), "945664" } } };
var doubanApi = new DoubanApi(loggerFactory);
var tmdbApi = new TmdbApi(loggerFactory);
var omdbApi = new OmdbApi(loggerFactory);
var httpClientFactory = new DefaultHttpClientFactory();
var libraryManagerStub = new Mock<ILibraryManager>();
Task.Run(async () =>
{
var provider = new MovieProvider(httpClientFactory, loggerFactory, libraryManagerStub.Object, doubanApi, tmdbApi, omdbApi);
var result = await provider.GetMetadata(info, CancellationToken.None);
Assert.IsNotNull(result);
var str = result.ToJson();
Console.WriteLine(result.ToJson());
}).GetAwaiter().GetResult();
}
}
}

View File

@ -62,7 +62,7 @@ namespace Jellyfin.Plugin.MetaShark.Api
Regex regSite = new Regex(@"官方网站: (.+?)\n", RegexOptions.Compiled);
Regex regNameMath = new Regex(@"(.+第\w季|[\w\uff1a\uff01\uff0c\u00b7]+)\s*(.*)", RegexOptions.Compiled);
Regex regRole = new Regex(@"\([饰|配] (.+?)\)", RegexOptions.Compiled);
Regex regBackgroundImage = new Regex(@"url\((.+?)\)", RegexOptions.Compiled);
Regex regBackgroundImage = new Regex(@"url\(([^)]+?)\)$", RegexOptions.Compiled);
Regex regGender = new Regex(@"性别: \n(.+?)\n", RegexOptions.Compiled);
Regex regConstellation = new Regex(@"星座: \n(.+?)\n", RegexOptions.Compiled);
Regex regBirthdate = new Regex(@"出生日期: \n(.+?)\n", RegexOptions.Compiled);
@ -336,7 +336,7 @@ namespace Jellyfin.Plugin.MetaShark.Api
var celebrityImg = celebrityImgStr.GetMatchGroup(this.regBackgroundImage);
var celebrityNameStr = node.GetText("div.info a.name") ?? string.Empty;
var arr = celebrityNameStr.Split(" ");
var celebrityName = arr.Length > 1 ? arr[0] : string.Empty;
var celebrityName = arr.Length > 1 ? arr[0] : celebrityNameStr;
var celebrityRoleStr = node.GetText("div.info span.role") ?? string.Empty;
var celebrityRole = celebrityRoleStr.GetMatchGroup(this.regRole);
var arrRole = celebrityRoleStr.Split(" ");
@ -393,7 +393,10 @@ namespace Jellyfin.Plugin.MetaShark.Api
if (contentNode != null)
{
var img = contentNode.GetAttr("#headline .nbg img", "src") ?? string.Empty;
var name = contentNode.GetText("h1") ?? string.Empty;
var nameStr = contentNode.GetText("h1") ?? string.Empty;
var arr = nameStr.Split(" ");
var name = arr.Length > 1 ? arr[0] : nameStr;
var intro = contentNode.GetText("#intro span.all") ?? string.Empty;
if (string.IsNullOrEmpty(intro))
{
@ -434,6 +437,59 @@ namespace Jellyfin.Plugin.MetaShark.Api
return null;
}
public async Task<List<DoubanCelebrity>> SearchCelebrityAsync(string keyword, CancellationToken cancellationToken)
{
var list = new List<DoubanCelebrity>();
if (string.IsNullOrEmpty(keyword))
{
return list;
}
var cacheKey = $"search_celebrity_{keyword}";
var expiredOption = new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) };
List<DoubanCelebrity> searchResult;
if (_memoryCache.TryGetValue<List<DoubanCelebrity>>(cacheKey, out searchResult))
{
return searchResult;
}
keyword = HttpUtility.UrlEncode(keyword);
var url = $"https://movie.douban.com/celebrities/search?search_text={keyword}";
var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
return list;
}
var body = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
var context = BrowsingContext.New();
var doc = await context.OpenAsync(req => req.Content(body), cancellationToken).ConfigureAwait(false);
var elements = doc.QuerySelectorAll("div.article .result");
foreach (var el in elements)
{
var celebrity = new DoubanCelebrity();
var img = el.GetAttr("div.pic img", "src") ?? string.Empty;
var href = el.GetAttr("h3>a", "href") ?? string.Empty;
var cid = href.GetMatchGroup(this.regId);
var nameStr = el.GetText("h3>a") ?? string.Empty;
var arr = nameStr.Split(" ");
var name = arr.Length > 1 ? arr[0] : nameStr;
celebrity.Name = name;
celebrity.Img = img;
celebrity.Id = cid;
list.Add(celebrity);
}
_memoryCache.Set<List<DoubanCelebrity>>(cacheKey, list, expiredOption);
return list;
}
public async Task<List<DoubanPhoto>> GetWallpaperBySidAsync(string sid, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(sid))

View File

@ -26,9 +26,6 @@ public enum SomeOptions
public class PluginConfiguration : BasePluginConfiguration
{
public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
public string Pattern { get; set; } = @"(S\d{2}|E\d{2}|HDR|\d{3,4}p|WEBRip|WEB|YIFY|BrRip|BluRay|H265|H264|x264|AAC\.\d\.\d|AAC|HDTV|mkv|mp4)|(\[.*\])|(\-\w+|\{.*\}|【.*】|\(.*\)|\d+MB)|(\.|\-)";
public bool EnableTmdb { get; set; } = true;
public bool EnableTmdbSearch { get; set; } = false;

View File

@ -77,7 +77,8 @@
.addEventListener('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(TemplateConfig.pluginUniqueId).then(function (config) {
document.querySelector('#current_version').value = "v" + config.Version;
$('#current_version').text("v" + config.Version);
document.querySelector('#DoubanCookies').value = config.DoubanCookies;
document.querySelector('#EnableTmdb').checked = config.EnableTmdb;
document.querySelector('#EnableTmdbSearch').checked = config.EnableTmdbSearch;

View File

@ -40,9 +40,8 @@ namespace Jellyfin.Plugin.MetaShark.Controllers
/// <summary>
/// 获取弹幕文件内容.
/// 代理访问图片.
/// </summary>
/// <returns>xml弹幕文件内容</returns>
[Route("proxy/image")]
[HttpGet]
public async Task<Stream> ProxyImage(string url)
@ -54,7 +53,22 @@ namespace Jellyfin.Plugin.MetaShark.Controllers
}
var httpClient = GetHttpClient();
return await httpClient.GetStreamAsync(url).ConfigureAwait(false);
var response = await httpClient.GetAsync(url);
var stream = await response.Content.ReadAsStreamAsync();
Response.StatusCode = (int)response.StatusCode;
if (response.Content.Headers.ContentType != null)
{
Response.ContentType = response.Content.Headers.ContentType.ToString();
}
Response.ContentLength = response.Content.Headers.ContentLength;
foreach (var header in response.Headers)
{
Response.Headers.Add(header.Key, header.Value.First());
}
return stream;
}
private HttpClient GetHttpClient()

View File

@ -20,15 +20,39 @@ namespace Jellyfin.Plugin.MetaShark.Core
return null;
}
public static string GetTextOrDefault(this IElement el, string css, string defaultVal = "")
{
var node = el.QuerySelector(css);
if (node != null)
{
return node.Text().Trim();
}
return defaultVal;
}
public static string? GetAttr(this IElement el, string css, string attr)
{
var node = el.QuerySelector(css);
if (node != null)
{
return node.GetAttribute(attr).Trim();
var attrVal = node.GetAttribute(attr);
return attrVal != null ? attrVal.Trim() : null;
}
return null;
}
public static string? GetAttrOrDefault(this IElement el, string css, string attr, string defaultVal = "")
{
var node = el.QuerySelector(css);
if (node != null)
{
var attrVal = node.GetAttribute(attr);
return attrVal != null ? attrVal.Trim() : defaultVal;
}
return defaultVal;
}
}
}

View File

@ -50,14 +50,6 @@ namespace Jellyfin.Plugin.MetaShark.Providers
protected Regex regMetaSourcePrefix = new Regex(@"^\[.+\]", RegexOptions.Compiled);
public string Pattern
{
get
{
return this.config.Pattern;
}
}
protected PluginConfiguration config
{
get

View File

@ -125,6 +125,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
Genres = subject.Genres,
// ProductionLocations = [x?.Country],
PremiereDate = subject.ScreenTime,
Tagline = string.Empty,
};
if (!string.IsNullOrEmpty(subject.Imdb))
{
@ -175,8 +176,9 @@ namespace Jellyfin.Plugin.MetaShark.Providers
Tagline = movieResult.Tagline,
ProductionLocations = movieResult.ProductionCountries.Select(pc => pc.Name).ToArray()
};
var metadataResult = new MetadataResult<Movie>
result = new MetadataResult<Movie>
{
QueriedById = true,
HasMetadata = true,
ResultLanguage = info.MetadataLanguage,
Item = movie
@ -207,7 +209,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
result.AddPerson(person);
}
return metadataResult;
return result;
}
return result;

View File

@ -0,0 +1,111 @@
using Jellyfin.Plugin.MetaShark.Api;
using Jellyfin.Plugin.MetaShark.Core;
using Jellyfin.Plugin.MetaShark.Model;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TMDbLib.Objects.Languages;
namespace Jellyfin.Plugin.MetaShark.Providers
{
public class PersonImageProvider : BaseProvider, IRemoteImageProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="MovieImageProvider"/> class.
/// </summary>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{OddbImageProvider}"/> interface.</param>
/// <param name="doubanApi">Instance of <see cref="DoubanApi"/>.</param>
public PersonImageProvider(IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory, ILibraryManager libraryManager, DoubanApi doubanApi, TmdbApi tmdbApi, OmdbApi omdbApi)
: base(httpClientFactory, loggerFactory.CreateLogger<MovieImageProvider>(), libraryManager, doubanApi, tmdbApi, omdbApi)
{
}
/// <inheritdoc />
public string Name => Plugin.PluginName;
/// <inheritdoc />
public bool Supports(BaseItem item) => item is Person;
/// <inheritdoc />
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
yield return ImageType.Primary;
}
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var cid = item.GetProviderId(DoubanProviderId);
var metaSource = item.GetProviderId(Plugin.ProviderId);
this.Log($"GetImages for item: {item.Name} [metaSource]: {metaSource}");
if (!string.IsNullOrEmpty(cid))
{
var celebrity = await this._doubanApi.GetCelebrityAsync(cid, cancellationToken).ConfigureAwait(false);
if (celebrity != null)
{
return new List<RemoteImageInfo> {
new RemoteImageInfo
{
ProviderName = celebrity.Name,
Url = celebrity.Img,
Type = ImageType.Primary
}
};
}
}
this.Log($"Got images failed because the sid of \"{item.Name}\" is empty!");
return new List<RemoteImageInfo>();
}
/// <inheritdoc />
public async Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
this.Log("GetImageResponse url: {0}", url);
return await this._httpClientFactory.CreateClient().GetAsync(new Uri(url), cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Query for a background photo
/// </summary>
/// <param name="sid">a subject/movie id</param>
/// <param name="cancellationToken">Instance of the <see cref="CancellationToken"/> interface.</param>
private async Task<IEnumerable<RemoteImageInfo>> GetBackdrop(string sid, CancellationToken cancellationToken)
{
this.Log("GetBackdrop of sid: {0}", sid);
var photo = await this._doubanApi.GetWallpaperBySidAsync(sid, cancellationToken);
var list = new List<RemoteImageInfo>();
if (photo == null)
{
return list;
}
return photo.Where(x => x.Width > x.Height * 1.3).Select(x =>
{
return new RemoteImageInfo
{
ProviderName = Name,
Url = x.Large,
Type = ImageType.Backdrop,
};
});
}
}
}

View File

@ -1,4 +1,6 @@
using Jellyfin.Plugin.MetaShark.Api;
using System.Net.Mime;
using System.Xml.Schema;
using Jellyfin.Plugin.MetaShark.Api;
using Jellyfin.Plugin.MetaShark.Core;
using Jellyfin.Plugin.MetaShark.Model;
using MediaBrowser.Controller.Entities;
@ -42,7 +44,42 @@ namespace Jellyfin.Plugin.MetaShark.Providers
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken)
{
this.Log($"GetPersonSearchResults of [name]: {searchInfo.Name}");
return await Task.FromResult<IEnumerable<RemoteSearchResult>>(new List<RemoteSearchResult>());
var result = new List<RemoteSearchResult>();
var cid = searchInfo.GetProviderId(DoubanProviderId);
if (!string.IsNullOrEmpty(cid))
{
var celebrity = await this._doubanApi.GetCelebrityAsync(cid, cancellationToken).ConfigureAwait(false);
if (celebrity != null)
{
result.Add(new RemoteSearchResult
{
SearchProviderName = DoubanProviderName,
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, celebrity.Id } },
ImageUrl = this.GetProxyImageUrl(celebrity.Img),
Name = celebrity.Name,
}
);
return result;
}
}
var res = await this._doubanApi.SearchCelebrityAsync(searchInfo.Name, cancellationToken).ConfigureAwait(false);
result.AddRange(res.Take(this.config.MaxSearchResult).Select(x =>
{
return new RemoteSearchResult
{
SearchProviderName = DoubanProviderName,
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, x.Id } },
ImageUrl = this.GetProxyImageUrl(x.Img),
Name = x.Name,
};
}));
return result;
}
/// <inheritdoc />
@ -83,6 +120,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
}
}
result.QueriedById = true;
result.HasMetadata = true;
result.Item = item;

View File

@ -60,8 +60,8 @@ rm -rf MediaBrowser*.dll Microsoft*.dll Newtonsoft*.dll System*.dll Emby*.dll Je
3. Move folder `metashark` to jellyfin `data/plugins` folder
## QA
## FAQ
1. Plugin run in error: `System.BadImageFormatException: Bad IL format.`
#### Plugin run in error: `System.BadImageFormatException: Bad IL format.`
> Remove all hidden file in `metashark` plugin folder
Remove all hidden file and `meta.json` in `metashark` plugin folder

View File

@ -1,7 +1,7 @@
{
"category": "Metadata",
"changelog": "NA",
"description": "jellyfin电影元数据插件影片信息只要从豆瓣获取并由TMDB补充缺失的季数据和剧集数据。",
"description": "jellyfin电影元数据插件影片信息只要从豆瓣获取并由TMDB补充缺失的剧集数据。",
"guid": "9a19103f-16f7-4668-be54-9a1e7a4f7556",
"imageUrl": "https://github.com/cxfksword/jellyfin-plugin-metashark/raw/main/doc/logo.png",
"name": "MetaShark",