Skip to content

Commit

Permalink
Tvdb v4 migration (#93)
Browse files Browse the repository at this point in the history
* Start migration to TVDB v4 api

* Incomplete migration

* Migration P2

* Migration push Part 3. Used my fork of TVDbSharper

* Migration Part 4

* Bug fixes

* Done Migration

* Removed redundant searchbyremoteid function

* Fixed Incorrect retrieval of episodeId

* Updated to use a more reliable way of converting ISO 639-2 to ISO 639-1

* Fixed TvdbSeriesProvider

* Fixed actors info and removed unneeded GetImages

* Removed unneeded api calls when getting season and series images.

* Changed to just convert from ISO 639-1 to ISO 639-2

* Switched to use GetCultureInfo instead of constructing

* TvdbMissingEpisodeProvider is now working some of the times

* Fixed TvdbMissingEpisodeProvider to work properly

* Remove some unneeded Api calls

* Added Content Rating retrieval

* Fixed missing season number for absolute in GetEpisodeTvdbId

* Added PR #91 fix

* Added suggested changes

* Add TvdbCultureInfo. Dupe of iso6382.txt from Jellyfin repo

* Added Country info

* Preliminary switch to tvdb-sdk-csharp

* Fixed csproj grouping

* Fixed login bug

* Re-add check for AverageRuntime for null

* Removed  maxSeasonNumber  in TvdbSeriesProvider

* Update to tvdb-sdk-csharp to 4.7.9

* Change to Tvdb.Sdk.dll

* Fixes series.AirDays assignment

* Comments

* Added Suggested changes and removed commented out code

* Removed IsValidEpisode function

* Refactor to use HttpClientFactory
  • Loading branch information
scampower3 authored Feb 11, 2024
1 parent 1721725 commit 1b6930b
Show file tree
Hide file tree
Showing 16 changed files with 2,294 additions and 847 deletions.
16 changes: 12 additions & 4 deletions Jellyfin.Plugin.Tvdb/Configuration/PluginConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Plugins;

namespace Jellyfin.Plugin.Tvdb.Configuration
{
Expand All @@ -8,8 +8,16 @@ namespace Jellyfin.Plugin.Tvdb.Configuration
public class PluginConfiguration : BasePluginConfiguration
{
/// <summary>
/// Gets or sets the tvdb api key.
/// Gets the tvdb api key for project.
/// </summary>
public string ApiKey { get; set; } = "OG4V3YJ3FAP7FP2K";
public const string ProjectApiKey = "";

/// <summary>
/// Gets or sets the tvdb api key for user.
/// </summary>
/// <remarks>
/// This is the subscriber's pin.
/// </remarks>
public string ApiKey { get; set; } = string.Empty;
}
}
}
4 changes: 2 additions & 2 deletions Jellyfin.Plugin.Tvdb/Configuration/config.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>TheTVDB</title>
Expand All @@ -18,7 +18,7 @@ <h2 class="sectionTitle">TheTVDB Settings:</h2>
<label for="txtTvdbApiKey">TheTvdb API Key:</label>
<input type="text" id="txtTvdbApiKey" required="required" is="emby-input"/>
<div class="fieldDescription">
TheTVDB Api Key
TheTVDB Api Key from Subscriptions.
</div>
</div>
<br/>
Expand Down
6 changes: 5 additions & 1 deletion Jellyfin.Plugin.Tvdb/Jellyfin.Plugin.Tvdb.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@

<ItemGroup>
<None Remove="Configuration\config.html" />
<None Remove="countries.json" />
<None Remove="iso6392.txt" />
<EmbeddedResource Include="Configuration\config.html" />
<EmbeddedResource Include="countries.json" />
<EmbeddedResource Include="iso6392.txt" />
</ItemGroup>

<ItemGroup>
Expand All @@ -21,7 +25,7 @@
<PackageReference Include="Jellyfin.Common" Version="10.*-*" />
<PackageReference Include="Jellyfin.Model" Version="10.*-*" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="TvDbSharper" Version="3.2.2" />
<PackageReference Include="Tvdb.Sdk" Version="4.7.9" />
</ItemGroup>

<!-- Code Analyzers-->
Expand Down
15 changes: 6 additions & 9 deletions Jellyfin.Plugin.Tvdb/Providers/TvdbEpisodeImageProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging;
using TvDbSharper;
using TvDbSharper.Dto;
using Tvdb.Sdk;

namespace Jellyfin.Plugin.Tvdb.Providers
{
Expand Down Expand Up @@ -95,13 +94,13 @@ await _tvdbClientManager
.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId, CultureInfo.InvariantCulture), language, cancellationToken)
.ConfigureAwait(false);

var image = GetImageInfo(episodeResult.Data);
var image = GetImageInfo(episodeResult);
if (image != null)
{
imageResult.Add(image);
}
}
catch (TvDbServerException e)
catch (Exception e)
{
_logger.LogError(e, "Failed to retrieve episode images for series {TvDbId}:{Name}", series.GetProviderId(TvdbPlugin.ProviderId), series.Name);
}
Expand All @@ -116,19 +115,17 @@ public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(new Uri(url), cancellationToken);
}

private RemoteImageInfo? GetImageInfo(EpisodeRecord episode)
private RemoteImageInfo? GetImageInfo(EpisodeExtendedRecord episode)
{
if (string.IsNullOrEmpty(episode.Filename))
if (string.IsNullOrEmpty(episode.Image))
{
return null;
}

return new RemoteImageInfo
{
Width = Convert.ToInt32(episode.ThumbWidth, CultureInfo.InvariantCulture),
Height = Convert.ToInt32(episode.ThumbHeight, CultureInfo.InvariantCulture),
ProviderName = Name,
Url = TvdbUtils.BannerUrl + episode.Filename,
Url = episode.Image,
Type = ImageType.Primary
};
}
Expand Down
156 changes: 70 additions & 86 deletions Jellyfin.Plugin.Tvdb/Providers/TvdbEpisodeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
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;
using TvDbSharper;
using TvDbSharper.Dto;
using Tvdb.Sdk;

namespace Jellyfin.Plugin.Tvdb.Providers
{
Expand Down Expand Up @@ -124,12 +125,12 @@ private async Task<MetadataResult<Episode>> GetCombinedEpisode(EpisodeInfo info,
results.Add(await GetEpisode(tempEpisodeInfo, cancellationToken).ConfigureAwait(false));
}

var result = CombineResults(info, results);
var result = CombineResults(results);

return result;
}

private MetadataResult<Episode> CombineResults(EpisodeInfo id, List<MetadataResult<Episode>> results)
private MetadataResult<Episode> CombineResults(List<MetadataResult<Episode>> results)
{
// Use first result as baseline
var result = results[0];
Expand Down Expand Up @@ -158,7 +159,7 @@ private async Task<MetadataResult<Episode>> GetEpisode(EpisodeInfo searchInfo, C
QueriedById = true
};

var seriesTvdbId = searchInfo.GetProviderId(TvdbPlugin.ProviderId);
var seriesTvdbId = searchInfo.SeriesProviderIds.FirstOrDefault(x => x.Key == TvdbPlugin.ProviderId).Value;
string? episodeTvdbId = null;
try
{
Expand All @@ -181,9 +182,9 @@ private async Task<MetadataResult<Episode>> GetEpisode(EpisodeInfo searchInfo, C
searchInfo.MetadataLanguage,
cancellationToken).ConfigureAwait(false);

result = MapEpisodeToResult(searchInfo, episodeResult.Data);
result = MapEpisodeToResult(searchInfo, episodeResult);
}
catch (TvDbServerException e)
catch (Exception e)
{
_logger.LogError(
e,
Expand All @@ -196,7 +197,7 @@ private async Task<MetadataResult<Episode>> GetEpisode(EpisodeInfo searchInfo, C
return result;
}

private static MetadataResult<Episode> MapEpisodeToResult(EpisodeInfo id, EpisodeRecord episode)
private static MetadataResult<Episode> MapEpisodeToResult(EpisodeInfo id, EpisodeExtendedRecord episode)
{
var result = new MetadataResult<Episode>
{
Expand All @@ -209,116 +210,99 @@ private static MetadataResult<Episode> MapEpisodeToResult(EpisodeInfo id, Episod
AirsBeforeEpisodeNumber = episode.AirsBeforeEpisode,
AirsAfterSeasonNumber = episode.AirsAfterSeason,
AirsBeforeSeasonNumber = episode.AirsBeforeSeason,
Name = episode.EpisodeName,
Overview = episode.Overview,
CommunityRating = (float?)episode.SiteRating,
OfficialRating = episode.ContentRating,
// Tvdb uses 3 letter code for language (prob ISO 639-2)
// Reverts to OriginalName if no translation is found
Name = episode.Translations.NameTranslations.FirstOrDefault(x => string.Equals(x.Language, TvdbUtils.NormalizeLanguageToTvdb(id.MetadataLanguage), StringComparison.OrdinalIgnoreCase))?.Name ?? episode.Name,
Overview = episode.Translations.OverviewTranslations?.FirstOrDefault(x => string.Equals(x.Language, TvdbUtils.NormalizeLanguageToTvdb(id.MetadataLanguage), StringComparison.OrdinalIgnoreCase))?.Overview ?? episode.Overview
}
};
result.ResetPeople();

var item = result.Item;
item.SetProviderId(TvdbPlugin.ProviderId, episode.Id.ToString(CultureInfo.InvariantCulture));
item.SetProviderId(MetadataProvider.Imdb, episode.ImdbId);
var imdbID = episode.RemoteIds.FirstOrDefault(x => string.Equals(x.SourceName, "IMDB", StringComparison.OrdinalIgnoreCase))?.Id;
if (!string.IsNullOrEmpty(imdbID))
{
item.SetProviderId(MetadataProvider.Imdb, imdbID);
}

if (string.Equals(id.SeriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
{
item.IndexNumber = Convert.ToInt32(episode.DvdEpisodeNumber ?? episode.AiredEpisodeNumber, CultureInfo.InvariantCulture);
item.ParentIndexNumber = episode.DvdSeason ?? episode.AiredSeason;
var dvdInfo = episode.Seasons.FirstOrDefault(x => string.Equals(x.Type.Name, "dvd", StringComparison.OrdinalIgnoreCase));
if (dvdInfo is null)
{
item.IndexNumber = episode.Number;
}
else
{
item.IndexNumber = Convert.ToInt32(dvdInfo.Number, CultureInfo.InvariantCulture);
}

item.ParentIndexNumber = episode.SeasonNumber;
}
else if (string.Equals(id.SeriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase))
{
if (episode.AbsoluteNumber.GetValueOrDefault() != 0)
var absoluteInfo = episode.Seasons.FirstOrDefault(x => string.Equals(x.Type.Name, "absolute", StringComparison.OrdinalIgnoreCase));
if (absoluteInfo is not null)
{
item.IndexNumber = episode.AbsoluteNumber;
item.IndexNumber = Convert.ToInt32(absoluteInfo.Number, CultureInfo.InvariantCulture);
}
}
else if (episode.AiredEpisodeNumber.HasValue)
{
item.IndexNumber = episode.AiredEpisodeNumber;
}
else if (episode.AiredSeason.HasValue)
else
{
item.ParentIndexNumber = episode.AiredSeason;
item.IndexNumber = episode.Number;
item.ParentIndexNumber = episode.SeasonNumber;
}

if (DateTime.TryParse(episode.FirstAired, out var date))
if (DateTime.TryParse(episode.Aired, out var date))
{
// dates from tvdb are UTC but without offset or Z
item.PremiereDate = date;
item.ProductionYear = date.Year;
}

foreach (var director in episode.Directors)
{
result.AddPerson(new PersonInfo
{
Name = director,
Type = PersonType.Director
});
}

// 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)
if (episode.Characters is not null)
{
var currentActor = episode.GuestStars[i];
var roleStartIndex = currentActor.IndexOf('(', StringComparison.Ordinal);

if (roleStartIndex == -1)
for (var i = 0; i < episode.Characters.Count; ++i)
{
result.AddPerson(new PersonInfo
var currentActor = episode.Characters[i];
if (string.Equals(currentActor.PeopleType, "Actor", StringComparison.OrdinalIgnoreCase))
{
Type = PersonType.GuestStar,
Name = currentActor,
Role = string.Empty
});
continue;
}

var roles = new List<string> { currentActor.Substring(roleStartIndex + 1) };

// Fetch all roles
for (var j = i + 1; j < episode.GuestStars.Length; ++j)
{
var currentRole = episode.GuestStars[j];
var roleEndIndex = currentRole.Contains(')', StringComparison.Ordinal);

if (!roleEndIndex)
result.AddPerson(new PersonInfo
{
Type = PersonType.Actor,
Name = currentActor.PersonName,
Role = currentActor.Name
});
}
else if (string.Equals(currentActor.PeopleType, "Director", StringComparison.OrdinalIgnoreCase))
{
roles.Add(currentRole);
continue;
result.AddPerson(new PersonInfo
{
Type = PersonType.Director,
Name = currentActor.PersonName
});
}
else if (string.Equals(currentActor.PeopleType, "Writer", StringComparison.OrdinalIgnoreCase))
{
result.AddPerson(new PersonInfo
{
Type = PersonType.Writer,
Name = currentActor.PersonName
});
}
else if (string.Equals(currentActor.PeopleType, "Guest Star", StringComparison.OrdinalIgnoreCase))
{
result.AddPerson(new PersonInfo
{
Type = PersonType.GuestStar,
Name = currentActor.PersonName,
Role = currentActor.Name
});
}

roles.Add(currentRole.TrimEnd(')'));
// Update the outer index (keep in mind it adds 1 after the iteration)
i = j;
break;
}

result.AddPerson(new PersonInfo
{
Type = PersonType.GuestStar,
Name = currentActor.Substring(0, roleStartIndex).Trim(),
Role = string.Join(", ", roles)
});
}

foreach (var writer in episode.Writers)
{
result.AddPerson(new PersonInfo
{
Name = writer,
Type = PersonType.Writer
});
}

result.ResultLanguage = episode.Language.EpisodeName;
return result;
}

Expand Down
Loading

0 comments on commit 1b6930b

Please sign in to comment.