fix: fix movie similar list only one. #66

This commit is contained in:
cxfksword 2024-02-28 11:01:21 +08:00
parent a9acabc056
commit 6f3863eaa7
16 changed files with 211 additions and 35 deletions

View File

@ -63,7 +63,7 @@ namespace Jellyfin.Plugin.MetaShark.Test
var info = new MediaBrowser.Controller.Entities.Movies.Movie()
{
PreferredMetadataLanguage = "zh",
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), "752" }, { Plugin.ProviderId, MetaSource.Tmdb } }
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), "752" }, { Plugin.ProviderId, MetaSource.Tmdb.ToString() } }
};
var httpClientFactory = new DefaultHttpClientFactory();
var libraryManagerStub = new Mock<ILibraryManager>();

View File

@ -78,7 +78,7 @@ namespace Jellyfin.Plugin.MetaShark.Test
[TestMethod]
public void TestGetMetadataByTMDB()
{
var info = new MovieInfo() { Name = "人生大事", MetadataLanguage = "zh", ProviderIds = new Dictionary<string, string> { { Plugin.ProviderId, MetaSource.Tmdb }, { MetadataProvider.Tmdb.ToString(), "945664" } } };
var info = new MovieInfo() { Name = "人生大事", MetadataLanguage = "zh", ProviderIds = new Dictionary<string, string> { { Plugin.ProviderId, MetaSource.Tmdb.ToString() }, { MetadataProvider.Tmdb.ToString(), "945664" } } };
var httpClientFactory = new DefaultHttpClientFactory();
var libraryManagerStub = new Mock<ILibraryManager>();
var httpContextAccessorStub = new Mock<IHttpContextAccessor>();

View File

@ -35,7 +35,7 @@ namespace Jellyfin.Plugin.MetaShark.Test
var info = new MediaBrowser.Controller.Entities.Movies.Movie()
{
PreferredMetadataLanguage = "zh",
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), "67534" }, { Plugin.ProviderId, MetaSource.Tmdb } }
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), "67534" }, { Plugin.ProviderId, MetaSource.Tmdb.ToString() } }
};
var httpClientFactory = new DefaultHttpClientFactory();
var libraryManagerStub = new Mock<ILibraryManager>();

View File

@ -30,7 +30,7 @@ namespace Jellyfin.Plugin.MetaShark.Test
[TestMethod]
public void TestGetMetadata()
{
var info = new SeriesInfo() { Name = "一年一度喜剧大赛" };
var info = new SeriesInfo() { Name = "天下长河" };
var httpClientFactory = new DefaultHttpClientFactory();
var libraryManagerStub = new Mock<ILibraryManager>();
var httpContextAccessorStub = new Mock<IHttpContextAccessor>();

View File

@ -151,6 +151,18 @@ namespace Jellyfin.Plugin.MetaShark.Api
}
}
public async Task<List<DoubanSubject>> SearchMovieAsync(string keyword, CancellationToken cancellationToken)
{
var result = await this.SearchAsync(keyword, cancellationToken).ConfigureAwait(false);
return result.Where(x => x.Category == "电影").ToList();
}
public async Task<List<DoubanSubject>> SearchTVAsync(string keyword, CancellationToken cancellationToken)
{
var result = await this.SearchAsync(keyword, cancellationToken).ConfigureAwait(false);
return result.Where(x => x.Category == "电视剧").ToList();
}
public async Task<List<DoubanSubject>> SearchAsync(string keyword, CancellationToken cancellationToken)
{
var list = new List<DoubanSubject>();

View File

@ -6,9 +6,34 @@ using System.Threading.Tasks;
namespace Jellyfin.Plugin.MetaShark.Model
{
public static class MetaSource
public enum MetaSource
{
public const string Douban = "douban";
public const string Tmdb = "tmdb";
Douban,
Tmdb,
None
}
public static class MetaSourceExtensions
{
public static MetaSource ToMetaSource(this string? str)
{
if (str == null)
{
return MetaSource.None;
}
if (str.ToLower().StartsWith("douban"))
{
return MetaSource.Douban;
}
if (str.ToLower().StartsWith("tmdb"))
{
return MetaSource.Tmdb;
}
return MetaSource.None;
}
}
}

View File

@ -319,6 +319,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
break;
}
this.Log($"Not found tmdb id by [name]: {name} [year]: {year}");
return null;
}
@ -355,6 +356,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
break;
}
this.Log($"Not found tmdb id by imdb id: {imdb}");
return null;
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using Jellyfin.Plugin.MetaShark.Model;
using MediaBrowser.Model.Entities;
namespace Jellyfin.Plugin.MetaShark.Providers
{
public static class ProviderIdsExtensions
{
public static MetaSource GetMetaSource(this IHasProviderIds instance, string name)
{
var value = instance.GetProviderId(name);
return value.ToMetaSource();
}
public static void TryGetMetaSource(this Dictionary<string, string> dict, string name, out MetaSource metaSource)
{
if (dict.TryGetValue(name, out var value))
{
metaSource = value.ToMetaSource();
}
else
{
metaSource = MetaSource.None;
}
}
}
}

View File

@ -44,7 +44,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var sid = item.GetProviderId(DoubanProviderId);
var metaSource = item.GetProviderId(Plugin.ProviderId);
var metaSource = item.GetMetaSource(Plugin.ProviderId);
this.Log($"GetImages for item: {item.Name} [metaSource]: {metaSource}");
if (metaSource != MetaSource.Tmdb && !string.IsNullOrEmpty(sid))
{

View File

@ -42,13 +42,14 @@ namespace Jellyfin.Plugin.MetaShark.Providers
}
// 从douban搜索
// BUG注意ProviderIds传多个meta值会导致识别搜索时只返回一个结果
var res = await this._doubanApi.SearchAsync(info.Name, cancellationToken).ConfigureAwait(false);
var res = await this._doubanApi.SearchMovieAsync(info.Name, cancellationToken).ConfigureAwait(false);
result.AddRange(res.Take(Configuration.PluginConfiguration.MAX_SEARCH_RESULT).Select(x =>
{
return new RemoteSearchResult
{
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, x.Sid } },
// 注意jellyfin 会判断电影所有 provider id 是否有相同的,有相同的值就会认为是同一影片,会被合并不返回,必须保持 provider id 的唯一性
// 这里 Plugin.ProviderId 的值做这么复杂,是为了保持唯一
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, x.Sid }, { Plugin.ProviderId, $"{MetaSource.Douban}_{x.Sid}" } },
ImageUrl = this.GetProxyImageUrl(x.Img),
ProductionYear = x.Year,
Name = x.Name,
@ -64,7 +65,9 @@ namespace Jellyfin.Plugin.MetaShark.Providers
{
return new RemoteSearchResult
{
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), x.Id.ToString(CultureInfo.InvariantCulture) } },
// 注意jellyfin 会判断电影所有 provider id 是否有相同的,有相同的值就会认为是同一影片,会被合并不返回,必须保持 provider id 的唯一性
// 这里 Plugin.ProviderId 的值做这么复杂,是为了保持唯一
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), x.Id.ToString(CultureInfo.InvariantCulture) }, { Plugin.ProviderId, $"{MetaSource.Tmdb}_{x.Id}" } },
Name = string.Format("[TMDB]{0}", x.Title ?? x.OriginalTitle),
ImageUrl = this._tmdbApi.GetPosterUrl(x.PosterPath),
Overview = x.Overview,
@ -86,12 +89,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
// 使用刷新元数据时providerIds会保留旧有值只有识别/新增才会没值
var sid = info.GetProviderId(DoubanProviderId);
var tmdbId = info.GetProviderId(MetadataProvider.Tmdb);
var metaSource = info.GetProviderId(Plugin.ProviderId);
// 用于修正识别时指定tmdb没法读取tmdb数据的BUG。。。两个合在一起太难了。。。
if (string.IsNullOrEmpty(metaSource) && info.Name.StartsWith("[TMDB]"))
{
metaSource = MetaSource.Tmdb;
}
var metaSource = info.GetMetaSource(Plugin.ProviderId);
// 注意会存在元数据有tmdbId但metaSource没值的情况之前由TMDB插件刮削导致
var hasTmdbMeta = metaSource == MetaSource.Tmdb && !string.IsNullOrEmpty(tmdbId);
var hasDoubanMeta = metaSource != MetaSource.Tmdb && !string.IsNullOrEmpty(sid);
@ -120,7 +118,8 @@ namespace Jellyfin.Plugin.MetaShark.Providers
var movie = new Movie
{
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, subject.Sid }, { Plugin.ProviderId, MetaSource.Douban } },
// 这里 Plugin.ProviderId 的值做这么复杂,是为了保持唯一
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, subject.Sid }, { Plugin.ProviderId, $"{MetaSource.Douban}_{subject.Sid}" } },
Name = subject.Name,
OriginalTitle = subject.OriginalName,
CommunityRating = subject.Rating,
@ -233,7 +232,8 @@ namespace Jellyfin.Plugin.MetaShark.Providers
movie.SetProviderId(MetadataProvider.Tmdb, tmdbId);
movie.SetProviderId(MetadataProvider.Imdb, movieResult.ImdbId);
movie.SetProviderId(Plugin.ProviderId, MetaSource.Tmdb);
// 这里 Plugin.ProviderId 的值做这么复杂,是为了保持唯一
movie.SetProviderId(Plugin.ProviderId, $"{MetaSource.Tmdb}_{tmdbId}");
// 获取电影系列信息
if (this.config.EnableTmdbCollection && movieResult.BelongsToCollection != null)

View File

@ -37,7 +37,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
{
var list = new List<RemoteImageInfo>();
var cid = item.GetProviderId(DoubanProviderId);
var metaSource = item.GetProviderId(Plugin.ProviderId);
var metaSource = item.GetMetaSource(Plugin.ProviderId);
this.Log($"GetImages for item: {item.Name} [metaSource]: {metaSource}");
if (!string.IsNullOrEmpty(cid))
{

View File

@ -44,7 +44,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
this.Log($"GetSeasonImages for item: {item.Name} number: {item.IndexNumber}");
var season = (Season)item;
var series = season.Series;
var metaSource = series.GetProviderId(Plugin.ProviderId);
var metaSource = series.GetMetaSource(Plugin.ProviderId);
// get image from douban
var sid = item.GetProviderId(DoubanProviderId);

View File

@ -42,7 +42,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
// 使用刷新元数据时之前识别的seasonNumber会保留不会被覆盖
info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out var seriesTmdbId);
info.SeriesProviderIds.TryGetValue(Plugin.ProviderId, out var metaSource);
info.SeriesProviderIds.TryGetMetaSource(Plugin.ProviderId, out var metaSource);
info.SeriesProviderIds.TryGetValue(DoubanProviderId, out var sid);
var seasonNumber = info.IndexNumber; // S00/Season 00特典目录会为0
var seasonSid = info.GetProviderId(DoubanProviderId);

View File

@ -44,7 +44,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var sid = item.GetProviderId(DoubanProviderId);
var metaSource = item.GetProviderId(Plugin.ProviderId);
var metaSource = item.GetMetaSource(Plugin.ProviderId);
this.Log($"GetImages for item: {item.Name} [metaSource]: {metaSource}");
if (metaSource != MetaSource.Tmdb && !string.IsNullOrEmpty(sid))
{

View File

@ -41,12 +41,13 @@ namespace Jellyfin.Plugin.MetaShark.Providers
}
// 从douban搜索
var res = await this._doubanApi.SearchAsync(info.Name, cancellationToken).ConfigureAwait(false);
var res = await this._doubanApi.SearchTVAsync(info.Name, cancellationToken).ConfigureAwait(false);
result.AddRange(res.Take(Configuration.PluginConfiguration.MAX_SEARCH_RESULT).Select(x =>
{
return new RemoteSearchResult
{
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, x.Sid } },
// 这里 Plugin.ProviderId 的值做这么复杂,是为了和电影保持一致并唯一
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, x.Sid }, { Plugin.ProviderId, $"{MetaSource.Douban}_{x.Sid}" } },
ImageUrl = this.GetProxyImageUrl(x.Img),
ProductionYear = x.Year,
Name = x.Name,
@ -61,7 +62,8 @@ namespace Jellyfin.Plugin.MetaShark.Providers
{
return new RemoteSearchResult
{
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), x.Id.ToString(CultureInfo.InvariantCulture) } },
// 这里 Plugin.ProviderId 的值做这么复杂,是为了和电影保持一致并唯一
ProviderIds = new Dictionary<string, string> { { MetadataProvider.Tmdb.ToString(), x.Id.ToString(CultureInfo.InvariantCulture) }, { Plugin.ProviderId, $"{MetaSource.Tmdb}_{x.Id}" } },
Name = string.Format("[TMDB]{0}", x.Name ?? x.OriginalName),
ImageUrl = this._tmdbApi.GetPosterUrl(x.PosterPath),
Overview = x.Overview,
@ -82,12 +84,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
var sid = info.GetProviderId(DoubanProviderId);
var tmdbId = info.GetProviderId(MetadataProvider.Tmdb);
var metaSource = info.GetProviderId(Plugin.ProviderId);
// 用于修正识别时指定tmdb没法读取tmdb数据的BUG。。。两个合在一起太难了。。。
if (string.IsNullOrEmpty(metaSource) && info.Name.StartsWith("[TMDB]"))
{
metaSource = MetaSource.Tmdb;
}
var metaSource = info.GetMetaSource(Plugin.ProviderId);
// 注意会存在元数据有tmdbId但metaSource没值的情况之前由TMDB插件刮削导致
var hasTmdbMeta = metaSource == MetaSource.Tmdb && !string.IsNullOrEmpty(tmdbId);
var hasDoubanMeta = metaSource != MetaSource.Tmdb && !string.IsNullOrEmpty(sid);
@ -110,7 +107,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
var seriesName = RemoveSeasonSubfix(subject.Name);
var item = new Series
{
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, subject.Sid }, { Plugin.ProviderId, MetaSource.Douban } },
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, subject.Sid }, { Plugin.ProviderId, $"{MetaSource.Douban}_{subject.Sid}" } },
Name = seriesName,
OriginalTitle = RemoveSeasonSubfix(subject.OriginalName),
CommunityRating = subject.Rating,
@ -341,7 +338,7 @@ namespace Jellyfin.Plugin.MetaShark.Providers
series.SetProviderId(MetadataProvider.Tvdb, ids.TvdbId);
}
}
series.SetProviderId(Plugin.ProviderId, MetaSource.Tmdb);
series.SetProviderId(Plugin.ProviderId, $"{MetaSource.Tmdb}_{seriesResult.Id}");
series.OfficialRating = this.GetTmdbOfficialRatingByData(seriesResult, preferredCountryCode);

View File

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using Jellyfin.Plugin.MetaShark.Core;
using Jellyfin.Plugin.MetaShark.Providers;
using Jellyfin.Plugin.MetaShark.Model;
namespace Jellyfin.Plugin.MetaShark.ScheduledTasks
{
public class FixMovieSimilarListTask : IScheduledTask
{
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
public string Key => $"{Plugin.PluginName}FixMovieSimilarList";
public string Name => "修复电影推荐列表";
public string Description => $"修复电影推荐列表只有一部影片的问题。";
public string Category => Plugin.PluginName;
/// <summary>
/// Initializes a new instance of the <see cref="FixMovieSimilarListTask"/> class.
/// </summary>
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
public FixMovieSimilarListTask(ILoggerFactory loggerFactory, ILibraryManager libraryManager)
{
_logger = loggerFactory.CreateLogger<FixMovieSimilarListTask>();
_libraryManager = libraryManager;
}
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new List<TaskTriggerInfo>();
// yield return new TaskTriggerInfo
// {
// Type = TaskTriggerInfo.TriggerWeekly,
// DayOfWeek = DayOfWeek.Monday,
// TimeOfDayTicks = TimeSpan.FromHours(4).Ticks
// };
}
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
await Task.Yield();
progress?.Report(0);
// 只有电影有问题
var items = _libraryManager.GetItemList(new InternalItemsQuery
{
// MediaTypes = new[] { MediaType.Video },
HasAnyProviderId = new Dictionary<string, string>() { { Plugin.ProviderId, string.Empty } },
IncludeItemTypes = new[] { BaseItemKind.Movie }
}).ToList();
_logger.LogInformation("Fix movie similar list for {0} videos.", items.Count);
var successCount = 0;
var failCount = 0;
foreach (var (item, idx) in items.WithIndex())
{
cancellationToken.ThrowIfCancellationRequested();
progress?.Report((double)idx / items.Count * 100);
try
{
// 判断电影带有旧的元数据,进行替换处理
var sid = item.GetProviderId(BaseProvider.DoubanProviderId);
var tmdbId = item.GetProviderId(MetadataProvider.Tmdb);
var providerVal = item.GetProviderId(Plugin.ProviderId);
if (providerVal == "douban" && !string.IsNullOrEmpty(sid))
{
var detail = this._libraryManager.GetItemById(item.Id);
detail.SetProviderId(Plugin.ProviderId, $"{MetaSource.Douban}_{sid}");
await detail.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
if (providerVal == "tmdb" && !string.IsNullOrEmpty(tmdbId))
{
var detail = this._libraryManager.GetItemById(item.Id);
detail.SetProviderId(Plugin.ProviderId, $"{MetaSource.Tmdb}_{tmdbId}");
await detail.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
successCount++;
}
catch (Exception ex)
{
_logger.LogError(ex, "Fix movie similar list for movie {0}: {1}", item.Name, ex.Message);
failCount++;
}
}
progress?.Report(100);
_logger.LogInformation("Exectue task completed. success: {0} fail: {1}", successCount, failCount);
}
}
}