jellyfin-plugin-metashark/Jellyfin.Plugin.MetaShark/Providers/SeasonProvider.cs

283 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Jellyfin.Plugin.MetaShark.Api;
using Jellyfin.Plugin.MetaShark.Core;
using Jellyfin.Plugin.MetaShark.Model;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TMDbLib.Objects.Find;
using TMDbLib.Objects.TvShows;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
namespace Jellyfin.Plugin.MetaShark.Providers
{
public class SeasonProvider : BaseProvider, IRemoteMetadataProvider<Season, SeasonInfo>
{
public SeasonProvider(IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory, ILibraryManager libraryManager, IHttpContextAccessor httpContextAccessor, DoubanApi doubanApi, TmdbApi tmdbApi, OmdbApi omdbApi)
: base(httpClientFactory, loggerFactory.CreateLogger<SeasonProvider>(), libraryManager, httpContextAccessor, doubanApi, tmdbApi, omdbApi)
{
}
public string Name => Plugin.PluginName;
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeasonInfo info, CancellationToken cancellationToken)
{
this.Log($"GetSeasonSearchResults of [name]: {info.Name}");
return await Task.FromResult(Enumerable.Empty<RemoteSearchResult>());
}
/// <inheritdoc />
public async Task<MetadataResult<Season>> GetMetadata(SeasonInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<Season>();
info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out var seriesTmdbId);
info.SeriesProviderIds.TryGetValue(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);
this.Log($"GetSeasonMetaData of [name]: {info.Name} number: {info.IndexNumber} seriesTmdbId: {seriesTmdbId} sid: {sid} metaSource: {metaSource}");
if (metaSource != MetaSource.Tmdb && !string.IsNullOrEmpty(sid))
{
// 从sereis获取正确名称info.Name当是标准格式如S01等时会变成第x季非标准名称没法识别时默认文件名
var series = await this._doubanApi.GetMovieAsync(sid, cancellationToken).ConfigureAwait(false);
if (series == null)
{
return result;
}
var seasonName = RemoveSeasonSubfix(series.Name);
// TODO:季文件夹名称不规范没法拿到seasonNumber尝试从文件名猜出
// 没有季id但存在tmdbid尝试从tmdb获取对应季的年份信息用于从豆瓣搜索对应季数据
if (string.IsNullOrEmpty(seasonSid))
{
var seasonYear = 0;
if (!string.IsNullOrEmpty(seriesTmdbId) && (seasonNumber.HasValue && seasonNumber > 0))
{
var season = await this._tmdbApi
.GetSeasonAsync(seriesTmdbId.ToInt(), seasonNumber.Value, info.MetadataLanguage, info.MetadataLanguage, cancellationToken)
.ConfigureAwait(false);
seasonYear = season?.AirDate?.Year ?? 0;
}
if (!string.IsNullOrEmpty(seasonName) && seasonYear > 0)
{
seasonSid = await this.GuestDoubanSeasonByYearAsync(seasonName, seasonYear, cancellationToken).ConfigureAwait(false);
}
}
// 获取季豆瓣数据
if (!string.IsNullOrEmpty(seasonSid))
{
var subject = await this._doubanApi.GetMovieAsync(seasonSid, cancellationToken).ConfigureAwait(false);
if (subject != null)
{
subject.Celebrities = await this._doubanApi.GetCelebritiesBySidAsync(seasonSid, cancellationToken).ConfigureAwait(false);
var movie = new Season
{
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, subject.Sid } },
Name = subject.Name,
OriginalTitle = subject.OriginalName,
CommunityRating = subject.Rating,
Overview = subject.Intro,
ProductionYear = subject.Year,
Genres = subject.Genres,
PremiereDate = subject.ScreenTime, // 发行日期
IndexNumber = info.IndexNumber,
};
result.Item = movie;
result.HasMetadata = true;
subject.LimitDirectorCelebrities.Take(this.config.MaxCastMembers).ToList().ForEach(c => result.AddPerson(new PersonInfo
{
Name = c.Name,
Type = c.RoleType,
Role = c.Role,
ImageUrl = c.Img,
ProviderIds = new Dictionary<string, string> { { DoubanProviderId, c.Id } },
}));
return result;
}
}
else
{
this.Log($"GetSeasonMetaData of [name]: {info.Name} not found douban season id!");
}
// tmdb有数据豆瓣找不到尝试获取tmdb的季数据
if (string.IsNullOrEmpty(seasonSid) && !string.IsNullOrWhiteSpace(seriesTmdbId) && (seasonNumber.HasValue && seasonNumber > 0))
{
var tmdbResult = await this.GetMetadataByTmdb(info, seriesTmdbId, seasonNumber.Value, cancellationToken).ConfigureAwait(false);
if (tmdbResult != null)
{
return tmdbResult;
}
}
// 从豆瓣获取不到季信息
return result;
}
// series使用TMDB元数据来源
// tmdb季级没有对应id只通过indexNumber区分
if (!string.IsNullOrWhiteSpace(seriesTmdbId) && (seasonNumber.HasValue && seasonNumber > 0))
{
var tmdbResult = await this.GetMetadataByTmdb(info, seriesTmdbId, seasonNumber.Value, cancellationToken).ConfigureAwait(false);
if (tmdbResult != null)
{
return tmdbResult;
}
}
return result;
}
public async Task<MetadataResult<Season>?> GetMetadataByTmdb(SeasonInfo info, string seriesTmdbId, int seasonNumber, CancellationToken cancellationToken)
{
var seasonResult = await this._tmdbApi
.GetSeasonAsync(seriesTmdbId.ToInt(), seasonNumber, info.MetadataLanguage, info.MetadataLanguage, cancellationToken)
.ConfigureAwait(false);
if (seasonResult == null)
{
this.Log($"Not found season from TMDB. {info.Name} seriesTmdbId: {seriesTmdbId} seasonNumber: {seasonNumber}");
return null;
}
var result = new MetadataResult<Season>();
result.HasMetadata = true;
result.Item = new Season
{
Name = seasonResult.Name,
IndexNumber = seasonNumber,
Overview = seasonResult.Overview,
PremiereDate = seasonResult.AirDate,
ProductionYear = seasonResult.AirDate?.Year,
};
if (!string.IsNullOrEmpty(seasonResult.ExternalIds?.TvdbId))
{
result.Item.SetProviderId(MetadataProvider.Tvdb, seasonResult.ExternalIds.TvdbId);
}
foreach (var person in GetPersons(seasonResult))
{
result.AddPerson(person);
}
return result;
}
private IEnumerable<PersonInfo> GetPersons(TvSeason item)
{
// 演员
if (item.Credits?.Cast != null)
{
foreach (var actor in item.Credits.Cast.OrderBy(a => a.Order).Take(this.config.MaxCastMembers))
{
var personInfo = new PersonInfo
{
Name = actor.Name.Trim(),
Role = actor.Character,
Type = PersonType.Actor,
SortOrder = actor.Order,
};
if (!string.IsNullOrWhiteSpace(actor.ProfilePath))
{
personInfo.ImageUrl = this._tmdbApi.GetProfileUrl(actor.ProfilePath);
}
if (actor.Id > 0)
{
personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture));
}
yield return personInfo;
}
}
// 导演
if (item.Credits?.Crew != null)
{
var keepTypes = new[]
{
PersonType.Director,
PersonType.Writer,
PersonType.Producer
};
foreach (var person in item.Credits.Crew)
{
// Normalize this
var type = MapCrewToPersonType(person);
if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase)
&& !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
continue;
}
var personInfo = new PersonInfo
{
Name = person.Name.Trim(),
Role = person.Job,
Type = type
};
if (!string.IsNullOrWhiteSpace(person.ProfilePath))
{
personInfo.ImageUrl = this._tmdbApi.GetPosterUrl(person.ProfilePath);
}
if (person.Id > 0)
{
personInfo.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture));
}
yield return personInfo;
}
}
}
/// <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);
}
private void Log(string? message, params object?[] args)
{
this._logger.LogInformation($"[MetaShark] {message}", args);
}
}
}