-
-
Notifications
You must be signed in to change notification settings - Fork 35
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 Boxset support #168
Draft
scampower3
wants to merge
9
commits into
jellyfin:master
Choose a base branch
from
scampower3:boxset-support
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+578
−0
Draft
Add Boxset support #168
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
1a709bb
Start boxset support
scampower3 fc76dee
Add required apis
scampower3 1b4b51e
Add preliminary Boxset provider
scampower3 4c1cd57
Add some support for boxset
scampower3 ae03e2b
Cleanup TvdbBoxSetImageprovider
scampower3 e165c3b
Add scheduled task to create collections
scampower3 ecb8485
Cleanup
scampower3 035e23a
Dont create collection if only 2 items
scampower3 291acf3
use CancellationToken
scampower3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbBoxSetExternalId.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using MediaBrowser.Controller.Entities.Movies; | ||
using MediaBrowser.Controller.Providers; | ||
using MediaBrowser.Model.Entities; | ||
using MediaBrowser.Model.Providers; | ||
|
||
namespace Jellyfin.Plugin.Tvdb.Providers.ExternalId | ||
{ | ||
/// <summary> | ||
/// The TvdbBoxSetExternalId class. | ||
/// </summary> | ||
public class TvdbBoxSetExternalId : IExternalId | ||
{ | ||
/// <inheritdoc /> | ||
public string ProviderName => TvdbPlugin.ProviderName + " Numerical"; | ||
|
||
/// <inheritdoc /> | ||
public string Key => TvdbPlugin.ProviderId; | ||
|
||
/// <inheritdoc /> | ||
public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet; | ||
|
||
/// <inheritdoc /> | ||
public string? UrlFormatString => null; | ||
|
||
/// <inheritdoc /> | ||
public bool Supports(IHasProviderIds item) => item is BoxSet; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
Jellyfin.Plugin.Tvdb/Providers/ExternalId/TvdbBoxSetSlugExternalId.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using MediaBrowser.Controller.Entities.Movies; | ||
using MediaBrowser.Controller.Providers; | ||
using MediaBrowser.Model.Entities; | ||
using MediaBrowser.Model.Providers; | ||
|
||
namespace Jellyfin.Plugin.Tvdb.Providers.ExternalId | ||
{ | ||
/// <summary> | ||
/// The TvdbBoxSetSlugExternalId class. | ||
/// </summary> | ||
public class TvdbBoxSetSlugExternalId : IExternalId | ||
{ | ||
/// <inheritdoc /> | ||
public string ProviderName => TvdbPlugin.ProviderName; | ||
|
||
/// <inheritdoc /> | ||
public string Key => TvdbPlugin.SlugProviderId; | ||
|
||
/// <inheritdoc /> | ||
public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet; | ||
|
||
/// <inheritdoc /> | ||
public string? UrlFormatString => TvdbUtils.TvdbBaseUrl + "lists/{0}"; | ||
|
||
/// <inheritdoc /> | ||
public bool Supports(IHasProviderIds item) => item is BoxSet; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using MediaBrowser.Common.Net; | ||
using MediaBrowser.Controller.Entities; | ||
using MediaBrowser.Controller.Entities.Movies; | ||
using MediaBrowser.Controller.Providers; | ||
using MediaBrowser.Model.Entities; | ||
using MediaBrowser.Model.Providers; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Jellyfin.Plugin.Tvdb.Providers | ||
{ | ||
/// <summary> | ||
/// The TvdbBoxSetProvider class. | ||
/// </summary> | ||
public class TvdbBoxSetImageProvider : IRemoteImageProvider | ||
{ | ||
private readonly IHttpClientFactory _httpClientFactory; | ||
private readonly ILogger<TvdbEpisodeProvider> _logger; | ||
private readonly TvdbClientManager _tvdbClientManager; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TvdbBoxSetImageProvider"/> class. | ||
/// </summary> | ||
/// <param name="httpClientFactory">Instance of <see cref="IHttpClientFactory"/>.</param> | ||
/// <param name="logger">Instance of <see cref="ILogger{TvdbEpisodeProvider}"/>.</param> | ||
/// <param name="tvdbClientManager">Instance of <see cref="TvdbClientManager"/>.</param> | ||
public TvdbBoxSetImageProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbEpisodeProvider> logger, TvdbClientManager tvdbClientManager) | ||
{ | ||
_httpClientFactory = httpClientFactory; | ||
_logger = logger; | ||
_tvdbClientManager = tvdbClientManager; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public string Name => TvdbPlugin.ProviderName; | ||
|
||
/// <inheritdoc /> | ||
public bool Supports(BaseItem item) | ||
{ | ||
return item is BoxSet; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IEnumerable<ImageType> GetSupportedImages(BaseItem item) | ||
{ | ||
yield return ImageType.Primary; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) | ||
{ | ||
if (!item.HasTvdbId()) | ||
{ | ||
return Enumerable.Empty<RemoteImageInfo>(); | ||
} | ||
|
||
var boxSetRecord = await _tvdbClientManager.GetBoxSetExtendedByIdAsync(item.GetTvdbId(), cancellationToken).ConfigureAwait(false); | ||
var remoteImageInfo = new RemoteImageInfo | ||
{ | ||
ProviderName = Name, | ||
Type = ImageType.Primary, | ||
Url = boxSetRecord.Image, | ||
}; | ||
|
||
return new List<RemoteImageInfo> { remoteImageInfo }; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) | ||
{ | ||
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(new Uri(url), cancellationToken); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using MediaBrowser.Common.Net; | ||
using MediaBrowser.Controller.Entities.Movies; | ||
using MediaBrowser.Controller.Library; | ||
using MediaBrowser.Controller.Providers; | ||
using MediaBrowser.Model.Providers; | ||
using Microsoft.Extensions.Logging; | ||
using Tvdb.Sdk; | ||
|
||
namespace Jellyfin.Plugin.Tvdb.Providers | ||
{ | ||
/// <summary> | ||
/// The TvdbBoxSetProvider class. | ||
/// </summary> | ||
public class TvdbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo> | ||
{ | ||
private readonly IHttpClientFactory _httpClientFactory; | ||
private readonly ILogger<TvdbEpisodeProvider> _logger; | ||
private readonly ILibraryManager _libraryManager; | ||
private readonly TvdbClientManager _tvdbClientManager; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TvdbBoxSetProvider"/> class. | ||
/// </summary> | ||
/// <param name="httpClientFactory">Instance of <see cref="IHttpClientFactory"/>.</param> | ||
/// <param name="logger">Instance of <see cref="ILogger{TvdbEpisodeProvider}"/>.</param> | ||
/// <param name="libraryManager">Instance of <see cref="ILibraryManager"/>.</param> | ||
/// <param name="tvdbClientManager">Instance of <see cref="TvdbClientManager"/>.</param> | ||
public TvdbBoxSetProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbEpisodeProvider> logger, ILibraryManager libraryManager, TvdbClientManager tvdbClientManager) | ||
{ | ||
_httpClientFactory = httpClientFactory; | ||
_logger = logger; | ||
_libraryManager = libraryManager; | ||
_tvdbClientManager = tvdbClientManager; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public string Name => TvdbPlugin.ProviderName; | ||
|
||
/// <inheritdoc /> | ||
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken) | ||
{ | ||
if (!string.IsNullOrEmpty(searchInfo.Name)) | ||
{ | ||
// Search for box sets | ||
return await FindBoxSet(searchInfo.Name, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); | ||
} | ||
|
||
// Return empty list if no results found | ||
return Enumerable.Empty<RemoteSearchResult>(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo info, CancellationToken cancellationToken) | ||
{ | ||
var result = new MetadataResult<BoxSet> | ||
{ | ||
QueriedById = true, | ||
}; | ||
// Tvdb Box sets are only supported by tvdb ids | ||
if (!info.HasTvdbId()) | ||
{ | ||
result.QueriedById = false; | ||
await Identify(info).ConfigureAwait(false); | ||
} | ||
|
||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
if (info.HasTvdbId()) | ||
{ | ||
result.Item = new BoxSet(); | ||
result.HasMetadata = true; | ||
|
||
await FetchBoxSetMetadata(result, info, cancellationToken) | ||
.ConfigureAwait(false); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private async Task<IEnumerable<RemoteSearchResult>> FindBoxSet(string name, string language, CancellationToken cancellationToken) | ||
{ | ||
_logger.LogDebug("TvdbSearch: Finding id for item: {Name}", name); | ||
var results = await FindBoxSetsInternal(name, language, cancellationToken).ConfigureAwait(false); | ||
|
||
return results; | ||
} | ||
|
||
private async Task<List<RemoteSearchResult>> FindBoxSetsInternal(string name, string language, CancellationToken cancellationToken) | ||
{ | ||
var parsedName = _libraryManager.ParseName(name); | ||
var comparableName = TvdbUtils.GetComparableName(parsedName.Name); | ||
|
||
var list = new List<Tuple<List<string>, RemoteSearchResult>>(); | ||
IReadOnlyList<SearchResult> result; | ||
try | ||
{ | ||
result = await _tvdbClientManager.GetBoxSetByNameAsync(comparableName, cancellationToken) | ||
.ConfigureAwait(false); | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.LogError(e, "No BoxSet results found for {Name}", comparableName); | ||
return new List<RemoteSearchResult>(); | ||
} | ||
|
||
foreach (var boxSetSearchResult in result) | ||
{ | ||
var tvdbTitles = new List<string> | ||
{ | ||
boxSetSearchResult.Translations.GetTranslatedNamedOrDefault(language) ?? boxSetSearchResult.Name | ||
}; | ||
if (boxSetSearchResult.Aliases is not null) | ||
{ | ||
tvdbTitles.AddRange(boxSetSearchResult.Aliases); | ||
} | ||
|
||
var remoteSearchResult = new RemoteSearchResult | ||
{ | ||
Name = tvdbTitles.FirstOrDefault(), | ||
SearchProviderName = Name | ||
}; | ||
|
||
if (!string.IsNullOrEmpty(boxSetSearchResult.Image_url)) | ||
{ | ||
remoteSearchResult.ImageUrl = boxSetSearchResult.Image_url; | ||
} | ||
|
||
remoteSearchResult.SetTvdbId(boxSetSearchResult.Tvdb_id); | ||
list.Add(new Tuple<List<string>, RemoteSearchResult>(tvdbTitles, remoteSearchResult)); | ||
} | ||
|
||
return list | ||
.OrderBy(i => i.Item1.Contains(name, StringComparer.OrdinalIgnoreCase) ? 0 : 1) | ||
.ThenBy(i => i.Item1.Any(title => title.Contains(parsedName.Name, StringComparison.OrdinalIgnoreCase)) ? 0 : 1) | ||
.ThenBy(i => i.Item1.Any(title => title.Contains(comparableName, StringComparison.OrdinalIgnoreCase)) ? 0 : 1) | ||
.ThenBy(i => list.IndexOf(i)) | ||
.Select(i => i.Item2) | ||
.ToList(); | ||
} | ||
|
||
private async Task Identify(BoxSetInfo info) | ||
{ | ||
if (info.HasTvdbId()) | ||
{ | ||
return; | ||
} | ||
|
||
var remoteSearchResults = await FindBoxSet(info.Name, info.MetadataLanguage, CancellationToken.None) | ||
.ConfigureAwait(false); | ||
|
||
var entry = remoteSearchResults.FirstOrDefault(); | ||
if (entry.HasTvdbId(out var tvdbId)) | ||
{ | ||
info.SetTvdbId(tvdbId); | ||
} | ||
} | ||
|
||
private async Task FetchBoxSetMetadata( | ||
MetadataResult<BoxSet> result, | ||
BoxSetInfo boxSetInfo, | ||
CancellationToken cancellationToken) | ||
{ | ||
var boxSetMetadata = result.Item; | ||
|
||
if (boxSetInfo.HasTvdbId(out var tvdbIdTxt)) | ||
{ | ||
boxSetMetadata.SetTvdbId(tvdbIdTxt); | ||
} | ||
|
||
if (string.IsNullOrWhiteSpace(tvdbIdTxt)) | ||
{ | ||
_logger.LogWarning("No valid tvdb id found for BoxSet {TvdbId}:{BoxSetName}", tvdbIdTxt, boxSetInfo.Name); | ||
return; | ||
} | ||
|
||
var tvdbId = Convert.ToInt32(tvdbIdTxt, CultureInfo.InvariantCulture); | ||
try | ||
{ | ||
var boxSetResult = | ||
await _tvdbClientManager | ||
.GetBoxSetExtendedByIdAsync(tvdbId, cancellationToken) | ||
.ConfigureAwait(false); | ||
MapBoxSetToResult(result, boxSetResult, boxSetInfo); | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.LogError(e, "Failed to retrieve BoxSet with id {TvdbId}:{BoxSetName}", tvdbId, boxSetInfo.Name); | ||
return; | ||
} | ||
Comment on lines
+192
to
+196
Check notice Code scanning / CodeQL Generic catch clause Note
Generic catch clause.
|
||
} | ||
|
||
private void MapBoxSetToResult(MetadataResult<BoxSet> result, ListExtendedRecord tvdbBoxSet, BoxSetInfo info) | ||
{ | ||
BoxSet boxSet = result.Item; | ||
boxSet.SetTvdbId(tvdbBoxSet.Id); | ||
// Tvdb uses 3 letter code for language (prob ISO 639-2) | ||
// Reverts to OriginalName if no translation is found | ||
// boxSet.Name = tvdbBoxSet.Translations.GetTranslatedNamedOrDefault(info.MetadataLanguage) ?? TvdbUtils.ReturnOriginalLanguageOrDefault(tvdbMovie.Name); | ||
boxSet.Name = tvdbBoxSet.Name; | ||
// boxSet.Overview = tvdbBoxSet.Translations.GetTranslatedOverviewOrDefault(info.MetadataLanguage); | ||
boxSet.Overview = tvdbBoxSet.Overview; | ||
boxSet.OriginalTitle = tvdbBoxSet.Name; | ||
result.ResultLanguage = info.MetadataLanguage; | ||
// Attempts to default to USA if not found | ||
boxSet.SetProviderIdIfHasValue(TvdbPlugin.SlugProviderId, tvdbBoxSet.Url); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) | ||
{ | ||
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(new Uri(url), cancellationToken); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check notice
Code scanning / CodeQL
Generic catch clause Note