Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Season support #154

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Jellyfin.Plugin.Tvdb/Configuration/PluginConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public int MetadataUpdateInHours
/// </summary>
public string FallbackLanguages { get; set; } = string.Empty;

/// <summary>
/// Gets or sets a value indicating whether to import season name.
/// </summary>
public bool ImportSeasonName { get; set; } = false;

/// <summary>
/// Gets or sets a value indicating whether to include missing specials.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions Jellyfin.Plugin.Tvdb/Configuration/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ <h2 class="sectionTitle">TheTVDB Settings:</h2>
If the preferred metadata language is not available, the plugin will attempt to retrieve metadata in these languages (in the order listed). Separate language codes with commas (e.g., en, fr, de, ja).
</div>
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="importSeasonName" />
<span>Import season name from provider.</span>
</label>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="includeMissingSpecials" />
<span>Include missing specials</span>
Expand Down Expand Up @@ -80,6 +84,7 @@ <h2 class="sectionTitle">TheTVDB Settings:</h2>
document.getElementById('cacheDurationInDays').value = config.CacheDurationInDays;
document.getElementById('metadataUpdateInHours').value = config.MetadataUpdateInHours;
document.getElementById('fallbackLanguages').value = config.FallbackLanguages;
document.getElementById('importSeasonName').checked = config.ImportSeasonName
document.getElementById('includeMissingSpecials').checked = config.IncludeMissingSpecials;
document.getElementById('fallbackToOriginalLanguage').checked = config.FallbackToOriginalLanguage;
Dashboard.hideLoadingMsg();
Expand All @@ -95,6 +100,7 @@ <h2 class="sectionTitle">TheTVDB Settings:</h2>
config.CacheDurationInDays = document.getElementById('cacheDurationInDays').value;
config.MetadataUpdateInHours = document.getElementById('metadataUpdateInHours').value;
config.FallbackLanguages = document.getElementById('fallbackLanguages').value;
config.ImportSeasonName = document.getElementById('importSeasonName').checked;
config.IncludeMissingSpecials = document.getElementById('includeMissingSpecials').checked;
config.FallbackToOriginalLanguage = document.getElementById('fallbackToOriginalLanguage').checked;
ApiClient.updatePluginConfiguration(TvdbPluginConfiguration.uniquePluginId, config).then(function (result) {
Expand Down
153 changes: 153 additions & 0 deletions Jellyfin.Plugin.Tvdb/Providers/TvdbSeasonProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
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 MediaBrowser.Common.Net;
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 Tvdb.Sdk;

namespace Jellyfin.Plugin.Tvdb.Providers
{
/// <summary>
/// The Tvdb Season Provider.
/// </summary>
public class TvdbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo>
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TvdbSeasonProvider> _logger;
private readonly ILibraryManager _libraryManager;
private readonly TvdbClientManager _tvdbClientManager;

/// <summary>
/// Initializes a new instance of the <see cref="TvdbSeasonProvider"/> class.
/// </summary>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{TvdbSeasonProvider}"/> interface.</param>
/// <param name="libraryManager">Instance of <see cref="ILibraryManager"/>.</param>
/// <param name="tvdbClientManager">Instance of <see cref="TvdbClientManager"/>.</param>
public TvdbSeasonProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbSeasonProvider> logger, ILibraryManager libraryManager, TvdbClientManager tvdbClientManager)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
_tvdbClientManager = tvdbClientManager;
_libraryManager = libraryManager;
}

/// <inheritdoc/>
public string Name => TvdbPlugin.ProviderName;

private static bool ImportSeasonName => TvdbPlugin.Instance?.Configuration.ImportSeasonName ?? false;

/// <inheritdoc/>
public async Task<MetadataResult<Season>> GetMetadata(SeasonInfo info, CancellationToken cancellationToken)
{
if (info.IndexNumber == null || !info.SeriesProviderIds.IsSupported())
{
_logger.LogDebug("No series identity found for {EpisodeName}", info.Name);
return new MetadataResult<Season>
{
QueriedById = true
};
}

int? seasonId = info.GetTvdbId();
string displayOrder;

// If the seasonId is 0, then we have to get the series metadata to get display order and then get the seasonId.
// If is automated is true, means that display order has changed,do the same as above.
if (seasonId == 0 || info.IsAutomated)
{
info.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out var seriesId);
if (string.IsNullOrWhiteSpace(seriesId))
{
_logger.LogDebug("No series identity found for {EpisodeName}", info.Name);
return new MetadataResult<Season>
{
QueriedById = true
};
}

var query = new InternalItemsQuery()
{
HasAnyProviderId = new Dictionary<string, string>
{
{ MetadataProvider.Tvdb.ToString(), seriesId }
}
};

var series = _libraryManager.GetItemList(query).OfType<Series>().FirstOrDefault();
displayOrder = series!.DisplayOrder;
if (string.IsNullOrWhiteSpace(displayOrder))
{
displayOrder = "official";
}

var seriesInfo = await _tvdbClientManager.GetSeriesExtendedByIdAsync(series.GetTvdbId(), string.Empty, cancellationToken, small: true)
.ConfigureAwait(false);
seasonId = seriesInfo.Seasons.FirstOrDefault(s => s.Number == info.IndexNumber && string.Equals(s.Type.Type, displayOrder, StringComparison.OrdinalIgnoreCase))?.Id;

if (seasonId == null)
{
_logger.LogDebug("No season identity found for {SeasonName}", info.Name);
return new MetadataResult<Season>
{
QueriedById = true
};
}
}

var seasonInfo = await _tvdbClientManager.GetSeasonByIdAsync(seasonId ?? 0, string.Empty, cancellationToken)
.ConfigureAwait(false);

return MapSeasonToResult(info, seasonInfo);
}

private MetadataResult<Season> MapSeasonToResult(SeasonInfo id, SeasonExtendedRecord season)
{
var result = new MetadataResult<Season>
{
HasMetadata = true,
Item = new Season
{
IndexNumber = id.IndexNumber,
// Tvdb uses 3 letter code for language (prob ISO 639-2)
// Reverts to OriginalName if no translation is found
// Overview = season.Translations.GetTranslatedOverviewOrDefault(id.MetadataLanguage),
}
};

var item = result.Item;
item.SetTvdbId(season.Id);

if (ImportSeasonName)
{
// item.Name = season.Translations.GetTranslatedNamedOrDefault(id.MetadataLanguage) ?? TvdbUtils.ReturnOriginalLanguageOrDefault(season.Name);
item.OriginalTitle = season.Name;
}

return result;
}

/// <inheritdoc/>
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeasonInfo searchInfo, CancellationToken cancellationToken)
{
return Task.FromResult(Enumerable.Empty<RemoteSearchResult>());
}

/// <inheritdoc/>
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(new Uri(url), cancellationToken);
}
}
}