2019-01-30 22:28:43 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using MediaBrowser.Common.Net;
|
|
|
|
using MediaBrowser.Controller.Entities;
|
|
|
|
using MediaBrowser.Controller.Entities.TV;
|
|
|
|
using MediaBrowser.Controller.Providers;
|
|
|
|
using MediaBrowser.Model.Entities;
|
|
|
|
using MediaBrowser.Model.Providers;
|
|
|
|
using Microsoft.Extensions.Logging;
|
2019-02-07 21:42:02 +00:00
|
|
|
using TvDbSharper;
|
2019-01-30 22:28:43 +00:00
|
|
|
using TvDbSharper.Dto;
|
|
|
|
|
2019-02-11 18:19:26 +00:00
|
|
|
namespace MediaBrowser.Providers.TV.TheTVDB
|
2019-01-30 22:28:43 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Class RemoteEpisodeProvider
|
|
|
|
/// </summary>
|
2019-04-30 18:08:59 +00:00
|
|
|
public class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
|
2019-01-30 22:28:43 +00:00
|
|
|
{
|
|
|
|
private readonly IHttpClient _httpClient;
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
private readonly TvDbClientManager _tvDbClientManager;
|
|
|
|
|
2019-02-20 18:08:47 +00:00
|
|
|
public TvdbEpisodeProvider(IHttpClient httpClient, ILogger<TvdbEpisodeProvider> logger, TvDbClientManager tvDbClientManager)
|
2019-01-30 22:28:43 +00:00
|
|
|
{
|
|
|
|
_httpClient = httpClient;
|
|
|
|
_logger = logger;
|
2019-02-15 19:11:27 +00:00
|
|
|
_tvDbClientManager = tvDbClientManager;
|
2019-01-30 22:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
|
|
|
|
{
|
|
|
|
var list = new List<RemoteSearchResult>();
|
|
|
|
|
|
|
|
// The search query must either provide an episode number or date
|
|
|
|
if (!searchInfo.IndexNumber.HasValue || !searchInfo.PremiereDate.HasValue)
|
|
|
|
{
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds))
|
|
|
|
{
|
2019-02-07 21:42:02 +00:00
|
|
|
try
|
2019-01-30 22:28:43 +00:00
|
|
|
{
|
2019-02-08 18:48:18 +00:00
|
|
|
var episodeTvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb);
|
|
|
|
if (string.IsNullOrEmpty(episodeTvdbId))
|
|
|
|
{
|
2019-02-20 18:08:47 +00:00
|
|
|
searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(),
|
|
|
|
out var seriesTvdbId);
|
2019-02-20 18:35:47 +00:00
|
|
|
episodeTvdbId = await _tvDbClientManager
|
|
|
|
.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken)
|
|
|
|
.ConfigureAwait(false);
|
2019-02-08 18:48:18 +00:00
|
|
|
if (string.IsNullOrEmpty(episodeTvdbId))
|
|
|
|
{
|
2019-02-11 17:47:40 +00:00
|
|
|
_logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}",
|
2019-02-20 18:08:47 +00:00
|
|
|
searchInfo.ParentIndexNumber, searchInfo.IndexNumber, seriesTvdbId);
|
2019-02-08 18:48:18 +00:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 18:35:47 +00:00
|
|
|
|
|
|
|
var episodeResult = await _tvDbClientManager.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId),
|
|
|
|
searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
|
2019-02-07 21:42:02 +00:00
|
|
|
var metadataResult = MapEpisodeToResult(searchInfo, episodeResult.Data);
|
2019-01-30 22:28:43 +00:00
|
|
|
|
2019-02-07 21:42:02 +00:00
|
|
|
if (metadataResult.HasMetadata)
|
2019-01-30 22:28:43 +00:00
|
|
|
{
|
2019-02-07 21:42:02 +00:00
|
|
|
var item = metadataResult.Item;
|
|
|
|
|
|
|
|
list.Add(new RemoteSearchResult
|
|
|
|
{
|
|
|
|
IndexNumber = item.IndexNumber,
|
|
|
|
Name = item.Name,
|
|
|
|
ParentIndexNumber = item.ParentIndexNumber,
|
|
|
|
PremiereDate = item.PremiereDate,
|
|
|
|
ProductionYear = item.ProductionYear,
|
|
|
|
ProviderIds = item.ProviderIds,
|
|
|
|
SearchProviderName = Name,
|
|
|
|
IndexNumberEnd = item.IndexNumberEnd
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (TvDbServerException e)
|
|
|
|
{
|
|
|
|
_logger.LogError(e, "Failed to retrieve episode with id {TvDbId}", searchInfo.IndexNumber);
|
2019-01-30 22:28:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
public string Name => "TheTVDB";
|
|
|
|
|
|
|
|
public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken)
|
|
|
|
{
|
|
|
|
var result = new MetadataResult<Episode>
|
|
|
|
{
|
|
|
|
QueriedById = true
|
|
|
|
};
|
|
|
|
|
|
|
|
if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
|
|
|
|
(searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
|
|
|
|
{
|
2019-02-07 21:42:02 +00:00
|
|
|
var tvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb);
|
|
|
|
try
|
|
|
|
{
|
2019-02-08 18:48:18 +00:00
|
|
|
if (string.IsNullOrEmpty(tvdbId))
|
|
|
|
{
|
2019-02-20 18:35:47 +00:00
|
|
|
tvdbId = await _tvDbClientManager
|
|
|
|
.GetEpisodeTvdbId(searchInfo, searchInfo.MetadataLanguage, cancellationToken)
|
|
|
|
.ConfigureAwait(false);
|
2019-02-08 18:48:18 +00:00
|
|
|
if (string.IsNullOrEmpty(tvdbId))
|
|
|
|
{
|
2019-02-11 17:47:40 +00:00
|
|
|
_logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}",
|
2019-02-08 18:48:18 +00:00
|
|
|
searchInfo.ParentIndexNumber, searchInfo.IndexNumber, tvdbId);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-07 21:42:02 +00:00
|
|
|
var episodeResult = await _tvDbClientManager.GetEpisodesAsync(
|
2019-02-12 18:50:19 +00:00
|
|
|
Convert.ToInt32(tvdbId), searchInfo.MetadataLanguage,
|
2019-02-20 18:35:47 +00:00
|
|
|
cancellationToken).ConfigureAwait(false);
|
2019-01-30 22:28:43 +00:00
|
|
|
|
2019-02-07 21:42:02 +00:00
|
|
|
result = MapEpisodeToResult(searchInfo, episodeResult.Data);
|
|
|
|
}
|
|
|
|
catch (TvDbServerException e)
|
|
|
|
{
|
|
|
|
_logger.LogError(e, "Failed to retrieve episode with id {TvDbId}", tvdbId);
|
|
|
|
}
|
2019-01-30 22:28:43 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-02-20 18:08:47 +00:00
|
|
|
_logger.LogDebug("No series identity found for {EpisodeName}", searchInfo.Name);
|
2019-01-30 22:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static MetadataResult<Episode> MapEpisodeToResult(EpisodeInfo id, EpisodeRecord episode)
|
|
|
|
{
|
|
|
|
var result = new MetadataResult<Episode>
|
|
|
|
{
|
|
|
|
HasMetadata = true,
|
|
|
|
Item = new Episode
|
|
|
|
{
|
|
|
|
IndexNumber = id.IndexNumber,
|
|
|
|
ParentIndexNumber = id.ParentIndexNumber,
|
|
|
|
IndexNumberEnd = id.IndexNumberEnd,
|
|
|
|
AirsBeforeEpisodeNumber = episode.AirsBeforeEpisode,
|
|
|
|
AirsAfterSeasonNumber = episode.AirsAfterSeason,
|
|
|
|
AirsBeforeSeasonNumber = episode.AirsBeforeSeason,
|
|
|
|
Name = episode.EpisodeName,
|
|
|
|
Overview = episode.Overview,
|
|
|
|
CommunityRating = (float?)episode.SiteRating,
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
result.ResetPeople();
|
|
|
|
|
|
|
|
var item = result.Item;
|
|
|
|
item.SetProviderId(MetadataProviders.Tvdb, episode.Id.ToString());
|
|
|
|
item.SetProviderId(MetadataProviders.Imdb, episode.ImdbId);
|
|
|
|
|
|
|
|
if (string.Equals(id.SeriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
|
|
|
item.IndexNumber = Convert.ToInt32(episode.DvdEpisodeNumber ?? episode.AiredEpisodeNumber);
|
|
|
|
item.ParentIndexNumber = episode.DvdSeason ?? episode.AiredSeason;
|
|
|
|
}
|
|
|
|
else if (episode.AiredEpisodeNumber.HasValue)
|
|
|
|
{
|
|
|
|
item.IndexNumber = episode.AiredEpisodeNumber;
|
|
|
|
}
|
|
|
|
else if (episode.AiredSeason.HasValue)
|
|
|
|
{
|
|
|
|
item.ParentIndexNumber = episode.AiredSeason;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DateTime.TryParse(episode.FirstAired, out var date))
|
|
|
|
{
|
2019-02-11 19:33:44 +00:00
|
|
|
// dates from tvdb are UTC but without offset or Z
|
2019-01-30 22:28:43 +00:00
|
|
|
item.PremiereDate = date;
|
|
|
|
item.ProductionYear = date.Year;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var director in episode.Directors)
|
|
|
|
{
|
|
|
|
result.AddPerson(new PersonInfo
|
|
|
|
{
|
|
|
|
Name = director,
|
|
|
|
Type = PersonType.Director
|
|
|
|
});
|
|
|
|
}
|
2019-02-02 15:12:16 +00:00
|
|
|
|
2019-08-15 12:54:22 +00:00
|
|
|
// GuestStars is a weird list of names and roles
|
|
|
|
// Example:
|
|
|
|
// 1: Some Actor (Role1
|
|
|
|
// 2: Role2
|
|
|
|
// 3: Role3)
|
|
|
|
// 4: Another Actor (Role1
|
|
|
|
// ...
|
|
|
|
for (var i = 0; i < episode.GuestStars.Length; ++i)
|
2019-01-30 22:28:43 +00:00
|
|
|
{
|
2019-08-15 12:54:22 +00:00
|
|
|
var currentActor = episode.GuestStars[i];
|
|
|
|
var roleStartIndex = currentActor.IndexOf('(');
|
2019-01-30 22:28:43 +00:00
|
|
|
|
2019-08-16 17:58:44 +00:00
|
|
|
if (roleStartIndex == -1)
|
2019-01-30 22:28:43 +00:00
|
|
|
{
|
2019-08-16 17:58:44 +00:00
|
|
|
result.AddPerson(new PersonInfo
|
2019-08-15 12:54:22 +00:00
|
|
|
{
|
2019-08-16 17:58:44 +00:00
|
|
|
Type = PersonType.GuestStar,
|
|
|
|
Name = currentActor,
|
|
|
|
Role = string.Empty
|
|
|
|
});
|
|
|
|
continue;
|
|
|
|
}
|
2019-08-15 17:54:01 +00:00
|
|
|
|
2019-08-16 17:58:44 +00:00
|
|
|
var roles = new List<string> {currentActor.Substring(roleStartIndex + 1)};
|
2019-08-15 17:54:01 +00:00
|
|
|
|
2019-08-16 17:58:44 +00:00
|
|
|
// Fetch all roles
|
|
|
|
for (var j = i + 1; j < episode.GuestStars.Length; ++j)
|
|
|
|
{
|
|
|
|
var currentRole = episode.GuestStars[j];
|
|
|
|
var roleEndIndex = currentRole.IndexOf(')');
|
|
|
|
|
2019-08-16 18:07:00 +00:00
|
|
|
if (roleEndIndex == -1)
|
2019-08-16 17:58:44 +00:00
|
|
|
{
|
2019-08-16 18:07:00 +00:00
|
|
|
roles.Add(currentRole);
|
2019-08-15 12:54:22 +00:00
|
|
|
}
|
2019-01-30 22:28:43 +00:00
|
|
|
|
2019-08-16 18:07:00 +00:00
|
|
|
roles.Add(currentRole.TrimEnd(')'));
|
|
|
|
// Update the outer index (keep in mind it adds 1 after the iteration)
|
|
|
|
i = j;
|
|
|
|
break;
|
2019-01-30 22:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result.AddPerson(new PersonInfo
|
|
|
|
{
|
|
|
|
Type = PersonType.GuestStar,
|
2019-08-16 17:58:44 +00:00
|
|
|
Name = currentActor.Substring(0, roleStartIndex).Trim(),
|
|
|
|
Role = string.Join(", ", roles)
|
2019-01-30 22:28:43 +00:00
|
|
|
});
|
|
|
|
}
|
2019-02-02 15:12:16 +00:00
|
|
|
|
2019-01-30 22:28:43 +00:00
|
|
|
foreach (var writer in episode.Writers)
|
|
|
|
{
|
|
|
|
result.AddPerson(new PersonInfo
|
|
|
|
{
|
|
|
|
Name = writer,
|
|
|
|
Type = PersonType.Writer
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-02-07 21:42:02 +00:00
|
|
|
result.ResultLanguage = episode.Language.EpisodeName;
|
2019-01-30 22:28:43 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
|
|
|
{
|
|
|
|
return _httpClient.GetResponse(new HttpRequestOptions
|
|
|
|
{
|
|
|
|
CancellationToken = cancellationToken,
|
|
|
|
Url = url
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public int Order => 0;
|
|
|
|
}
|
|
|
|
}
|