mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
298 lines
9.7 KiB
C#
298 lines
9.7 KiB
C#
// Kyoo - A portable and vast media library solution.
|
|
// Copyright (c) Kyoo.
|
|
//
|
|
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
//
|
|
// Kyoo is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// any later version.
|
|
//
|
|
// Kyoo is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Kyoo.Abstractions.Controllers;
|
|
using Kyoo.Abstractions.Models;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Logging;
|
|
using TMDbLib.Client;
|
|
using TMDbLib.Objects.Movies;
|
|
using TMDbLib.Objects.Search;
|
|
using TMDbLib.Objects.TvShows;
|
|
|
|
namespace Kyoo.TheMovieDb
|
|
{
|
|
/// <summary>
|
|
/// A metadata provider for TheMovieDb.
|
|
/// </summary>
|
|
public class TheMovieDbProvider : IMetadataProvider
|
|
{
|
|
/// <summary>
|
|
/// The API key used to authenticate with TheMovieDb API.
|
|
/// </summary>
|
|
private readonly string _apiKey;
|
|
|
|
/// <summary>
|
|
/// The logger to use in ase of issue.
|
|
/// </summary>
|
|
private readonly ILogger<TheMovieDbProvider> _logger;
|
|
|
|
/// <inheritdoc />
|
|
public Provider Provider => new()
|
|
{
|
|
Slug = "the-moviedb",
|
|
Name = "TheMovieDB",
|
|
Images = new Dictionary<int, string>
|
|
{
|
|
[Images.Logo] = "https://www.themoviedb.org/assets/2/v4/logos/v2/" +
|
|
"blue_short-8e7b30f73a4020692ccca9c88bafe5dcb6f8a62a4c6bc55cd9ba82bb2cd95f6c.svg"
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// Create a new <see cref="TheMovieDbProvider"/> using the given api key.
|
|
/// </summary>
|
|
/// <param name="config">The api key.</param>
|
|
/// <param name="logger">The logger to use in case of issue.</param>
|
|
public TheMovieDbProvider(IConfiguration config, ILogger<TheMovieDbProvider> logger)
|
|
{
|
|
_apiKey = config.GetValue<string>("THEMOVIEDB_APIKEY");
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task<T> Get<T>(T item)
|
|
where T : class, IResource
|
|
{
|
|
return item switch
|
|
{
|
|
Collection collection => _GetCollection(collection) as Task<T>,
|
|
Show show => _GetShow(show) as Task<T>,
|
|
Season season => _GetSeason(season) as Task<T>,
|
|
Episode episode => _GetEpisode(episode) as Task<T>,
|
|
People person => _GetPerson(person) as Task<T>,
|
|
Studio studio => _GetStudio(studio) as Task<T>,
|
|
_ => null
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a collection using it's id, if the id is not present in the collection, fallback to a name search.
|
|
/// </summary>
|
|
/// <param name="collection">The collection to search for.</param>
|
|
/// <returns>A collection containing metadata from TheMovieDb.</returns>
|
|
private async Task<Collection> _GetCollection(Collection collection)
|
|
{
|
|
if (!collection.TryGetID(Provider.Slug, out int id))
|
|
{
|
|
Collection found = (await _SearchCollections(collection.Name ?? collection.Slug)).FirstOrDefault();
|
|
if (found?.TryGetID(Provider.Slug, out id) != true)
|
|
return found;
|
|
}
|
|
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.GetCollectionAsync(id)).ToCollection(Provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a show using it's id, if the id is not present in the show, fallback to a title search.
|
|
/// </summary>
|
|
/// <param name="show">The show to search for.</param>
|
|
/// <returns>A show containing metadata from TheMovieDb.</returns>
|
|
private async Task<Show> _GetShow(Show show)
|
|
{
|
|
if (!show.TryGetID(Provider.Slug, out int id))
|
|
{
|
|
Show found = (await _SearchShows(show.Title ?? show.Slug, show.StartAir?.Year))
|
|
.FirstOrDefault(x => x.IsMovie == show.IsMovie);
|
|
if (found?.TryGetID(Provider.Slug, out id) != true)
|
|
return found;
|
|
}
|
|
|
|
TMDbClient client = new(_apiKey);
|
|
|
|
if (show.IsMovie)
|
|
{
|
|
return (await client
|
|
.GetMovieAsync(id, MovieMethods.AlternativeTitles | MovieMethods.Videos | MovieMethods.Credits))
|
|
?.ToShow(Provider);
|
|
}
|
|
|
|
return (await client
|
|
.GetTvShowAsync(id, TvShowMethods.AlternativeTitles | TvShowMethods.Videos | TvShowMethods.Credits))
|
|
?.ToShow(Provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a season using it's show and it's season number.
|
|
/// </summary>
|
|
/// <param name="season">The season to retrieve metadata for.</param>
|
|
/// <returns>A season containing metadata from TheMovieDb.</returns>
|
|
private async Task<Season> _GetSeason(Season season)
|
|
{
|
|
if (season.Show == null)
|
|
{
|
|
_logger.LogWarning("Metadata for a season was requested but it's show is not loaded. " +
|
|
"This is unsupported");
|
|
return null;
|
|
}
|
|
|
|
if (!season.Show.TryGetID(Provider.Slug, out int id))
|
|
return null;
|
|
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.GetTvSeasonAsync(id, season.SeasonNumber))
|
|
.ToSeason(id, Provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get an episode using it's show, it's season number and it's episode number.
|
|
/// Absolute numbering is not supported.
|
|
/// </summary>
|
|
/// <param name="episode">The episode to retrieve metadata for.</param>
|
|
/// <returns>An episode containing metadata from TheMovieDb.</returns>
|
|
private async Task<Episode> _GetEpisode(Episode episode)
|
|
{
|
|
if (episode.Show == null)
|
|
{
|
|
_logger.LogWarning("Metadata for an episode was requested but it's show is not loaded. " +
|
|
"This is unsupported");
|
|
return null;
|
|
}
|
|
if (!episode.Show.TryGetID(Provider.Slug, out int id)
|
|
|| episode.SeasonNumber == null || episode.EpisodeNumber == null)
|
|
return null;
|
|
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.GetTvEpisodeAsync(id, episode.SeasonNumber.Value, episode.EpisodeNumber.Value))
|
|
?.ToEpisode(id, Provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a person using it's id, if the id is not present in the person, fallback to a name search.
|
|
/// </summary>
|
|
/// <param name="person">The person to search for.</param>
|
|
/// <returns>A person containing metadata from TheMovieDb.</returns>
|
|
private async Task<People> _GetPerson(People person)
|
|
{
|
|
if (!person.TryGetID(Provider.Slug, out int id))
|
|
{
|
|
People found = (await _SearchPeople(person.Name ?? person.Slug)).FirstOrDefault();
|
|
if (found?.TryGetID(Provider.Slug, out id) != true)
|
|
return found;
|
|
}
|
|
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.GetPersonAsync(id)).ToPeople(Provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a studio using it's id, if the id is not present in the studio, fallback to a name search.
|
|
/// </summary>
|
|
/// <param name="studio">The studio to search for.</param>
|
|
/// <returns>A studio containing metadata from TheMovieDb.</returns>
|
|
private async Task<Studio> _GetStudio(Studio studio)
|
|
{
|
|
if (!studio.TryGetID(Provider.Slug, out int id))
|
|
{
|
|
Studio found = (await _SearchStudios(studio.Name ?? studio.Slug)).FirstOrDefault();
|
|
if (found?.TryGetID(Provider.Slug, out id) != true)
|
|
return found;
|
|
}
|
|
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.GetCompanyAsync(id)).ToStudio(Provider);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<ICollection<T>> Search<T>(string query)
|
|
where T : class, IResource
|
|
{
|
|
if (typeof(T) == typeof(Collection))
|
|
return (await _SearchCollections(query) as ICollection<T>)!;
|
|
if (typeof(T) == typeof(Show))
|
|
return (await _SearchShows(query) as ICollection<T>)!;
|
|
if (typeof(T) == typeof(People))
|
|
return (await _SearchPeople(query) as ICollection<T>)!;
|
|
if (typeof(T) == typeof(Studio))
|
|
return (await _SearchStudios(query) as ICollection<T>)!;
|
|
return ArraySegment<T>.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search for a collection using it's name as a query.
|
|
/// </summary>
|
|
/// <param name="query">The query to search for</param>
|
|
/// <returns>A list of collections containing metadata from TheMovieDb</returns>
|
|
private async Task<ICollection<Collection>> _SearchCollections(string query)
|
|
{
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.SearchCollectionAsync(query))
|
|
.Results
|
|
.Select(x => x.ToCollection(Provider))
|
|
.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search for a show using it's name as a query.
|
|
/// </summary>
|
|
/// <param name="query">The query to search for</param>
|
|
/// <param name="year">The year in witch the show has aired.</param>
|
|
/// <returns>A list of shows containing metadata from TheMovieDb</returns>
|
|
private async Task<ICollection<Show>> _SearchShows(string query, int? year = null)
|
|
{
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.SearchMultiAsync(query, year: year ?? 0))
|
|
.Results
|
|
.Select(x =>
|
|
{
|
|
return x switch
|
|
{
|
|
SearchTv tv => tv.ToShow(Provider),
|
|
SearchMovie movie => movie.ToShow(Provider),
|
|
_ => null
|
|
};
|
|
})
|
|
.Where(x => x != null)
|
|
.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search for people using there name as a query.
|
|
/// </summary>
|
|
/// <param name="query">The query to search for</param>
|
|
/// <returns>A list of people containing metadata from TheMovieDb</returns>
|
|
private async Task<ICollection<People>> _SearchPeople(string query)
|
|
{
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.SearchPersonAsync(query))
|
|
.Results
|
|
.Select(x => x.ToPeople(Provider))
|
|
.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search for studios using there name as a query.
|
|
/// </summary>
|
|
/// <param name="query">The query to search for</param>
|
|
/// <returns>A list of studios containing metadata from TheMovieDb</returns>
|
|
private async Task<ICollection<Studio>> _SearchStudios(string query)
|
|
{
|
|
TMDbClient client = new(_apiKey);
|
|
return (await client.SearchCompanyAsync(query))
|
|
.Results
|
|
.Select(x => x.ToStudio(Provider))
|
|
.ToArray();
|
|
}
|
|
}
|
|
}
|