diff --git a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbEpisodeExternalId.cs b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbEpisodeExternalId.cs
index 7f7f586..a19efa6 100644
--- a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbEpisodeExternalId.cs
+++ b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbEpisodeExternalId.cs
@@ -18,7 +18,7 @@ public class TvdbEpisodeExternalId : IExternalId
public ExternalIdMediaType? Type => ExternalIdMediaType.Episode;
///
- public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}";
+ public string? UrlFormatString => null;
///
public bool Supports(IHasProviderIds item) => item is Episode;
diff --git a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbMovieSlugExternalId.cs b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbMovieSlugExternalId.cs
index 32e448c..f34fc7a 100644
--- a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbMovieSlugExternalId.cs
+++ b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbMovieSlugExternalId.cs
@@ -9,7 +9,7 @@ namespace Jellyfin.Plugin.Tvdb.Providers.ExternalId
public class TvdbMovieSlugExternalId : IExternalId
{
///
- public string ProviderName => TvdbPlugin.ProviderName;
+ public string ProviderName => TvdbPlugin.ProviderName + " Slug";
///
public string Key => TvdbPlugin.SlugProviderId;
@@ -18,7 +18,7 @@ public class TvdbMovieSlugExternalId : IExternalId
public ExternalIdMediaType? Type => ExternalIdMediaType.Movie;
///
- public string? UrlFormatString => TvdbUtils.TvdbBaseUrl + "movies/{0}";
+ public string? UrlFormatString => null;
///
public bool Supports(IHasProviderIds item) => item is Movie;
diff --git a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbPersonExternalId.cs b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbPersonExternalId.cs
index f737cd7..807d4ec 100644
--- a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbPersonExternalId.cs
+++ b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbPersonExternalId.cs
@@ -25,7 +25,7 @@ public class TvdbPersonExternalId : IExternalId
public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
///
- public string? UrlFormatString => TvdbUtils.TvdbBaseUrl + "people/{0}";
+ public string? UrlFormatString => null;
///
public bool Supports(IHasProviderIds item) => item is Person;
diff --git a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeasonExternalId.cs b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeasonExternalId.cs
index 54317e3..16c6bdc 100644
--- a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeasonExternalId.cs
+++ b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeasonExternalId.cs
@@ -18,7 +18,7 @@ public class TvdbSeasonExternalId : IExternalId
public ExternalIdMediaType? Type => ExternalIdMediaType.Season;
///
- public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "dereferrer/season/{0}";
+ public string? UrlFormatString => null;
///
public bool Supports(IHasProviderIds item) => item is Season;
diff --git a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbExternalId.cs b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeriesExternalId.cs
similarity index 67%
rename from Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbExternalId.cs
rename to Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeriesExternalId.cs
index 58ed5fc..869f4ae 100644
--- a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbExternalId.cs
+++ b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeriesExternalId.cs
@@ -6,19 +6,19 @@
namespace Jellyfin.Plugin.Tvdb.Providers.ExternalId
{
///
- public class TvdbExternalId : IExternalId
+ public class TvdbSeriesExternalId : IExternalId
{
///
- public string ProviderName => TvdbPlugin.ProviderName;
+ public string ProviderName => TvdbPlugin.ProviderName + " Numerical";
///
public string Key => TvdbPlugin.ProviderId;
///
- public ExternalIdMediaType? Type => null;
+ public ExternalIdMediaType? Type => ExternalIdMediaType.Series;
///
- public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}";
+ public string? UrlFormatString => null;
///
public bool Supports(IHasProviderIds item) => item is Series;
diff --git a/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeriesSlugExternalId.cs b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeriesSlugExternalId.cs
new file mode 100644
index 0000000..fe76d5b
--- /dev/null
+++ b/Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbSeriesSlugExternalId.cs
@@ -0,0 +1,26 @@
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+
+namespace Jellyfin.Plugin.Tvdb.Providers.ExternalId
+{
+ ///
+ public class TvdbSeriesSlugExternalId : IExternalId
+ {
+ ///
+ public string ProviderName => TvdbPlugin.ProviderName + " Slug";
+
+ ///
+ public string Key => TvdbPlugin.SlugProviderId;
+
+ ///
+ public ExternalIdMediaType? Type => ExternalIdMediaType.Series;
+
+ ///
+ public string? UrlFormatString => null;
+
+ ///
+ public bool Supports(IHasProviderIds item) => item is Series;
+ }
+}
diff --git a/Jellyfin.Plugin.Tvdb/Providers/TvdbExternalUrlProvider.cs b/Jellyfin.Plugin.Tvdb/Providers/TvdbExternalUrlProvider.cs
new file mode 100644
index 0000000..d1510a8
--- /dev/null
+++ b/Jellyfin.Plugin.Tvdb/Providers/TvdbExternalUrlProvider.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Frozen;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+
+namespace Jellyfin.Plugin.Tvdb.Providers
+{
+ ///
+ /// Provides external urls for TheTVDB.
+ ///
+ public class TvdbExternalUrlProvider : IExternalUrlProvider
+ {
+ private readonly FrozenSet _supportedOrders = new[] { "official", "regional", "alternate", "altdvd", "dvd", "absolute" }.ToFrozenSet();
+
+ ///
+ public string Name => TvdbPlugin.ProviderName;
+
+ ///
+ public IEnumerable GetExternalUrls(BaseItem item)
+ {
+ var externalId = item.GetProviderId(TvdbPlugin.ProviderId);
+ var slugId = item.GetProviderId(TvdbPlugin.SlugProviderId);
+
+ switch (item)
+ {
+ case Series:
+ if (string.IsNullOrEmpty(slugId) && !string.IsNullOrEmpty(externalId))
+ {
+ yield return TvdbUtils.TvdbBaseUrl + $"?tab=series&id={externalId}";
+ }
+ else if (!string.IsNullOrEmpty(slugId))
+ {
+ yield return TvdbUtils.TvdbBaseUrl + $"series/{slugId}";
+ }
+
+ break;
+ case Season season:
+ season.Series.ProviderIds.TryGetValue(TvdbPlugin.SlugProviderId, out var seriesSlugId);
+ var displayOrder = season.Series.DisplayOrder;
+ if (string.IsNullOrEmpty(displayOrder))
+ {
+ displayOrder = "official";
+ }
+
+ if (_supportedOrders.Contains(displayOrder) && !string.IsNullOrEmpty(seriesSlugId))
+ {
+ yield return TvdbUtils.TvdbBaseUrl + $"series/{seriesSlugId}/seasons/{displayOrder}/{item.IndexNumber}";
+ }
+ else if (string.Equals(displayOrder, "official", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(externalId))
+ {
+ // This url format only works for official order
+ yield return TvdbUtils.TvdbBaseUrl + $"dereferrer/season/{externalId}";
+ }
+
+ break;
+ case Episode episode:
+ episode.Series.ProviderIds.TryGetValue(TvdbPlugin.SlugProviderId, out seriesSlugId);
+ if (!string.IsNullOrEmpty(seriesSlugId) && !string.IsNullOrEmpty(externalId))
+ {
+ yield return TvdbUtils.TvdbBaseUrl + $"series/{seriesSlugId}/episodes/{externalId}";
+ }
+ else if (!string.IsNullOrEmpty(externalId))
+ {
+ yield return TvdbUtils.TvdbBaseUrl + $"?tab=episode&id={externalId}";
+ }
+
+ break;
+ case Movie:
+ if (!string.IsNullOrEmpty(slugId))
+ {
+ yield return TvdbUtils.TvdbBaseUrl + $"movies/{slugId}";
+ }
+
+ break;
+ case Person:
+ if (!string.IsNullOrEmpty(externalId))
+ {
+ yield return TvdbUtils.TvdbBaseUrl + $"people/{externalId}";
+ }
+
+ break;
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs b/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs
index 67e2f93..9be0aa5 100644
--- a/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs
+++ b/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs
@@ -436,9 +436,11 @@ private void MapSeriesToResult(MetadataResult result, SeriesExtendedReco
result.ResultLanguage = info.MetadataLanguage;
series.AirDays = TvdbUtils.GetAirDays(tvdbSeries.AirsDays).ToArray();
series.AirTime = tvdbSeries.AirsTime;
- // series.CommunityRating = (float?)tvdbSeries.SiteRating;
// Attempts to default to USA if not found
series.OfficialRating = tvdbSeries.ContentRatings?.FirstOrDefault(x => string.Equals(x.Country, TvdbCultureInfo.GetCountryInfo(info.MetadataCountryCode)?.ThreeLetterISORegionName, StringComparison.OrdinalIgnoreCase))?.Name ?? tvdbSeries.ContentRatings?.FirstOrDefault(x => string.Equals(x.Country, "usa", StringComparison.OrdinalIgnoreCase))?.Name;
+
+ series.SetProviderIdIfHasValue(TvdbPlugin.SlugProviderId, tvdbSeries.Slug);
+
if (tvdbSeries.Lists is not null && tvdbSeries.Lists is JsonElement jsonElement)
{
var collections = jsonElement.Deserialize>();