mirror of
https://github.com/jellyfin/jellyfin-plugin-anidb.git
synced 2024-11-23 13:59:43 +00:00
Update to AniList APIv2
+ Fixing some wrong results + (Beta)Added AniList Person infos
This commit is contained in:
parent
46fc37da05
commit
57f5de0251
BIN
.vs/MediaBrowser.Plugins.Anime/v15/Server/sqlite3/storage.ide
Normal file
BIN
.vs/MediaBrowser.Plugins.Anime/v15/Server/sqlite3/storage.ide
Normal file
Binary file not shown.
0
.vs/MediaBrowser.Plugins.Anime/v15/sqlite3/db.lock
Normal file
0
.vs/MediaBrowser.Plugins.Anime/v15/sqlite3/db.lock
Normal file
Binary file not shown.
@ -1,110 +0,0 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
{
|
||||
public class AniListApiClient
|
||||
{
|
||||
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
|
||||
|
||||
public class AccessToken
|
||||
{
|
||||
public string access_token { get; set; }
|
||||
public int expires_in { get; set; }
|
||||
}
|
||||
|
||||
private const string ApiUrl = "https://anilist.co/api";
|
||||
private const string ClientId = "aphid-zmljg";
|
||||
private const string ClientSecret = "M37YedMnMm9DQ2D9pLoEeqM2Ul";
|
||||
|
||||
private static readonly string RequestTokenUrl = $"{ApiUrl}/auth/access_token?grant_type=client_credentials&client_id={ClientId}&client_secret={ClientSecret}";
|
||||
private static readonly string AnimeUrlFormat = $"{ApiUrl}/anime/{{0}}";
|
||||
private static readonly string SearchUrlFormat = $"{ApiUrl}/anime/search/{{0}}";
|
||||
|
||||
private readonly IHttpClient _http;
|
||||
private readonly ILogger _log;
|
||||
|
||||
private static string _accessToken;
|
||||
private static DateTime _accessTokenExpires;
|
||||
private IJsonSerializer _jsonSerializer;
|
||||
|
||||
public AniListApiClient(IHttpClient http, ILogManager logManager, IJsonSerializer jsonSerializer)
|
||||
{
|
||||
_http = http;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_log = logManager.GetLogger("AniList");
|
||||
}
|
||||
|
||||
public Task<Anime> GetAnime(string id)
|
||||
{
|
||||
return Get<Anime>(string.Format(AnimeUrlFormat, id));
|
||||
}
|
||||
|
||||
public Task<Anime[]> Search(string anime)
|
||||
{
|
||||
return Get<Anime[]>(string.Format(SearchUrlFormat, Uri.EscapeDataString(anime)));
|
||||
}
|
||||
|
||||
private async Task<T> Get<T>(string url, int attemptsRemaining = 2)
|
||||
{
|
||||
if (DateTime.Now > _accessTokenExpires)
|
||||
await RefreshAccessToken().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
var json = "";
|
||||
string urlWithToken = url;
|
||||
if (url.Contains("?"))
|
||||
urlWithToken += $"&access_token={_accessToken}";
|
||||
else
|
||||
urlWithToken += $"?access_token={_accessToken}";
|
||||
|
||||
var _webRequest = WebRequest.Create(@"" + Uri.EscapeUriString(urlWithToken));
|
||||
using (var _response = _webRequest.GetResponse())
|
||||
using (var _content = _response.GetResponseStream())
|
||||
using (var _reader = new StreamReader(_content))
|
||||
{
|
||||
json = _reader.ReadToEnd().Trim();
|
||||
}
|
||||
return _jsonSerializer.DeserializeFromString<T>(json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (attemptsRemaining <= 1)
|
||||
throw;
|
||||
|
||||
attemptsRemaining--;
|
||||
await RefreshAccessToken().ConfigureAwait(false);
|
||||
return await Get<T>(url, attemptsRemaining).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshAccessToken()
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new HttpRequestOptions
|
||||
{
|
||||
Url = RequestTokenUrl
|
||||
};
|
||||
|
||||
using (var response = await _http.Post(options).ConfigureAwait(false))
|
||||
{
|
||||
var credentials = _jsonSerializer.DeserializeFromStream<AccessToken>(response.Content);
|
||||
_accessToken = credentials.access_token;
|
||||
_accessTokenExpires = DateTime.Now + TimeSpan.FromSeconds(credentials.expires_in);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.ErrorException("Failed to retrieve API access token", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
{
|
||||
//public class AniListSeriesIdentityProvider : IItemIdentityProvider<SeriesInfo>
|
||||
//{
|
||||
// private readonly AniListApiClient _api;
|
||||
|
||||
// public AniListSeriesIdentityProvider(IHttpClient http, ILogManager logManager, IJsonSerializer jsonSerializer)
|
||||
// {
|
||||
// _api = new AniListApiClient(http, logManager, jsonSerializer);
|
||||
// }
|
||||
|
||||
// public async Task Identify(SeriesInfo info)
|
||||
// {
|
||||
// if (!string.IsNullOrEmpty(info.ProviderIds.GetOrDefault(ProviderNames.AniList)) )
|
||||
// return;
|
||||
|
||||
// if (string.IsNullOrEmpty(info.Name))
|
||||
// return;
|
||||
|
||||
// try
|
||||
// {
|
||||
// var search = await _api.Search(info.Name);
|
||||
|
||||
// var cleaned = AniDbTitleMatcher.GetComparableName(info.Name);
|
||||
// if (!search.Any() && String.Compare(info.Name, cleaned, StringComparison.OrdinalIgnoreCase) != 0)
|
||||
// search = await _api.Search(cleaned);
|
||||
|
||||
// var first = search.FirstOrDefault();
|
||||
|
||||
// if (first == null)
|
||||
// return;
|
||||
|
||||
// info.ProviderIds.Remove(ProviderNames.AniList);
|
||||
// info.ProviderIds.Add(ProviderNames.AniList, first.id.ToString());
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine(e);
|
||||
// // ignore
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
@ -6,34 +6,66 @@ using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Plugins.Anime.Configuration;
|
||||
using MediaBrowser.Plugins.Anime.Providers.AniDB.Identity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using MediaBrowser.Plugins.Anime.Providers.AniList.MediaBrowser.Plugins.Anime.Providers.AniList;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
//API v2
|
||||
namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
{
|
||||
public class AniListSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
|
||||
{
|
||||
private readonly IApplicationPaths _paths;
|
||||
private readonly AniListApiClient _api;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IApplicationPaths _paths;
|
||||
private readonly ILogger _log;
|
||||
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
|
||||
private readonly api _api;
|
||||
public int Order => -2;
|
||||
public string Name => "AniList";
|
||||
public static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
|
||||
|
||||
public AniListSeriesProvider(IHttpClient http, IApplicationPaths paths, ILogManager logManager, IJsonSerializer jsonSerializer)
|
||||
public AniListSeriesProvider(IApplicationPaths appPaths, IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer)
|
||||
{
|
||||
_httpClient = http;
|
||||
_paths = paths;
|
||||
_api = new AniListApiClient(http, logManager, jsonSerializer);
|
||||
_log = logManager.GetLogger("AniList");
|
||||
_httpClient = httpClient;
|
||||
_api = new api(jsonSerializer);
|
||||
_paths = appPaths;
|
||||
}
|
||||
|
||||
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new MetadataResult<Series>();
|
||||
|
||||
var aid = info.ProviderIds.GetOrDefault(ProviderNames.AniList);
|
||||
if (string.IsNullOrEmpty(aid))
|
||||
{
|
||||
_log.Info("Start AniList... Searching(" + info.Name + ")");
|
||||
aid = await _api.FindSeries(info.Name, cancellationToken);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(aid))
|
||||
{
|
||||
RootObject WebContent = await _api.WebRequestAPI(_api.AniList_anime_link.Replace("{0}",aid));
|
||||
result.Item = new Series();
|
||||
result.HasMetadata = true;
|
||||
|
||||
result.People = await _api.getPersonInfo(WebContent.data.Media.id);
|
||||
result.Item.ProviderIds.Add(ProviderNames.AniList, aid);
|
||||
result.Item.Overview = WebContent.data.Media.description;
|
||||
try
|
||||
{
|
||||
//AniList has a max rating of 5
|
||||
result.Item.CommunityRating = (WebContent.data.Media.averageScore/10);
|
||||
}
|
||||
catch (Exception) { }
|
||||
foreach (var genre in _api.Get_Genre(WebContent))
|
||||
result.Item.AddGenre(genre);
|
||||
GenreHelper.CleanupGenres(result.Item);
|
||||
StoreImageUrl(aid, WebContent.data.Media.coverImage.large, "image");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
|
||||
@ -43,171 +75,22 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
var aid = searchInfo.ProviderIds.GetOrDefault(ProviderNames.AniList);
|
||||
if (!string.IsNullOrEmpty(aid))
|
||||
{
|
||||
var anime = await _api.GetAnime(aid);
|
||||
if (anime != null && !results.ContainsKey(aid))
|
||||
results.Add(aid, ToSearchResult(anime));
|
||||
if (!results.ContainsKey(aid))
|
||||
results.Add(aid, await _api.GetAnime(aid));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(searchInfo.Name))
|
||||
{
|
||||
var search = await _api.Search(searchInfo.Name);
|
||||
foreach (var a in search)
|
||||
List<string> ids = await _api.Search_GetSeries_list(searchInfo.Name, cancellationToken);
|
||||
foreach (string a in ids)
|
||||
{
|
||||
if (!results.ContainsKey(a.id.ToString()))
|
||||
results.Add(a.id.ToString(), ToSearchResult(a));
|
||||
}
|
||||
|
||||
var cleaned = AniDbTitleMatcher.GetComparableName(searchInfo.Name);
|
||||
if (String.Compare(cleaned, searchInfo.Name, StringComparison.OrdinalIgnoreCase) != 0)
|
||||
{
|
||||
search = await _api.Search(cleaned);
|
||||
foreach (var a in search)
|
||||
{
|
||||
if (!results.ContainsKey(a.id.ToString()))
|
||||
results.Add(a.id.ToString(), ToSearchResult(a));
|
||||
}
|
||||
results.Add(a, await _api.GetAnime(a));
|
||||
}
|
||||
}
|
||||
|
||||
return results.Values;
|
||||
}
|
||||
|
||||
private RemoteSearchResult ToSearchResult(Anime anime)
|
||||
{
|
||||
var result = new RemoteSearchResult
|
||||
{
|
||||
Name = SelectName(anime, Plugin.Instance.Configuration.TitlePreference, "en")
|
||||
};
|
||||
|
||||
result.ImageUrl = anime.image_url_lge;
|
||||
result.SetProviderId(ProviderNames.AniList, anime.id.ToString());
|
||||
result.SearchProviderName = Name;
|
||||
|
||||
DateTime start;
|
||||
if (DateTime.TryParse(anime.start_date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out start))
|
||||
result.PremiereDate = start;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new MetadataResult<Series>();
|
||||
|
||||
var aid = info.ProviderIds.GetOrDefault(ProviderNames.AniList);
|
||||
if (string.IsNullOrEmpty(aid) && !string.IsNullOrEmpty(info.Name))
|
||||
{
|
||||
var search = await _api.Search(info.Name);
|
||||
foreach (var a in search)
|
||||
{
|
||||
if (string.IsNullOrEmpty(aid))
|
||||
{
|
||||
if (Equals_check.Compare_strings(a.title_english, info.Name))
|
||||
aid = a.id.ToString();
|
||||
|
||||
if (Equals_check.Compare_strings(a.title_japanese, info.Name))
|
||||
aid = a.id.ToString();
|
||||
|
||||
if (Equals_check.Compare_strings(a.title_romaji, info.Name))
|
||||
aid = a.id.ToString();
|
||||
_log.Log(LogSeverity.Info, a.title_romaji + "vs" + info.Name);
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(aid))
|
||||
{
|
||||
var cleaned = AniDbTitleMatcher.GetComparableName(Equals_check.clear_name(info.Name));
|
||||
if (String.Compare(cleaned, info.Name, StringComparison.OrdinalIgnoreCase) != 0)
|
||||
{
|
||||
search = await _api.Search(cleaned);
|
||||
foreach (var b in search)
|
||||
{
|
||||
if (Equals_check.Compare_strings(b.title_english, info.Name))
|
||||
aid = b.id.ToString();
|
||||
|
||||
if (Equals_check.Compare_strings(b.title_japanese, info.Name))
|
||||
aid = b.id.ToString();
|
||||
|
||||
if (Equals_check.Compare_strings(b.title_romaji, info.Name))
|
||||
aid = b.id.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(aid))
|
||||
{
|
||||
search = await _api.Search(Equals_check.clear_name(info.Name));
|
||||
foreach (var b in search)
|
||||
{
|
||||
if (Equals_check.Compare_strings(b.title_english, info.Name))
|
||||
aid = b.id.ToString();
|
||||
|
||||
if (Equals_check.Compare_strings(b.title_japanese, info.Name))
|
||||
aid = b.id.ToString();
|
||||
|
||||
if (Equals_check.Compare_strings(b.title_romaji, info.Name))
|
||||
aid = b.id.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(aid))
|
||||
{
|
||||
result.Item = new Series();
|
||||
result.HasMetadata = true;
|
||||
|
||||
result.Item.ProviderIds.Add(ProviderNames.AniList, aid);
|
||||
|
||||
var anime = await _api.GetAnime(aid);
|
||||
|
||||
result.Item.Name = SelectName(anime, Plugin.Instance.Configuration.TitlePreference, info.MetadataLanguage ?? "en");
|
||||
result.Item.Overview = anime.description;
|
||||
|
||||
DateTime start;
|
||||
if (DateTime.TryParse(anime.start_date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out start))
|
||||
result.Item.PremiereDate = start;
|
||||
|
||||
DateTime end;
|
||||
if (DateTime.TryParse(anime.end_date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out end))
|
||||
result.Item.EndDate = end;
|
||||
|
||||
if (anime.genres != null)
|
||||
{
|
||||
foreach (var genre in anime.genres)
|
||||
|
||||
if(!string.IsNullOrEmpty(genre))
|
||||
result.Item.AddGenre(genre);
|
||||
|
||||
GenreHelper.CleanupGenres(result.Item);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(anime.image_url_lge))
|
||||
StoreImageUrl(aid, anime.image_url_lge, "image");
|
||||
|
||||
if (!string.IsNullOrEmpty(anime.image_url_banner))
|
||||
StoreImageUrl(aid, anime.image_url_banner, "banner");
|
||||
|
||||
if (string.IsNullOrEmpty(result.Item.Name))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(anime.title_romaji))
|
||||
{
|
||||
result.Item.Name = anime.title_romaji;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string SelectName(Anime anime, TitlePreferenceType preference, string language)
|
||||
{
|
||||
if (preference == TitlePreferenceType.Localized && language == "en")
|
||||
return anime.title_english;
|
||||
|
||||
if (preference == TitlePreferenceType.Japanese)
|
||||
return anime.title_japanese;
|
||||
|
||||
return anime.title_romaji;
|
||||
}
|
||||
|
||||
private void StoreImageUrl(string series, string url, string type)
|
||||
{
|
||||
var path = Path.Combine(_paths.CachePath, "anilist", type, series + ".txt");
|
||||
@ -217,15 +100,6 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
File.WriteAllText(path, url);
|
||||
}
|
||||
|
||||
public static async Task<string> GetSeriesImage(IApplicationPaths paths, string series, string type)
|
||||
{
|
||||
var path = Path.Combine(paths.CachePath, "anilist", type, series + ".txt");
|
||||
if (File.Exists(path))
|
||||
return await Task.Run(() => File.ReadAllText(path));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
return _httpClient.GetResponse(new HttpRequestOptions
|
||||
@ -235,74 +109,60 @@ namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
ResourcePool = ResourcePool
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class AniListSeriesImageProvider : IRemoteImageProvider
|
||||
public class AniListSeriesImageProvider : IRemoteImageProvider
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly api _api;
|
||||
public AniListSeriesImageProvider(IHttpClient httpClient, IApplicationPaths appPaths, IJsonSerializer jsonSerializer)
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
_httpClient = httpClient;
|
||||
_appPaths = appPaths;
|
||||
_api = new api(jsonSerializer);
|
||||
}
|
||||
|
||||
public AniListSeriesImageProvider(IHttpClient httpClient, IApplicationPaths appPaths)
|
||||
public string Name => "AniList";
|
||||
|
||||
public bool Supports(BaseItem item) => item is Series || item is Season;
|
||||
|
||||
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
||||
{
|
||||
return new[] { ImageType.Primary };
|
||||
}
|
||||
|
||||
public Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
|
||||
{
|
||||
var seriesId = item.GetProviderId(ProviderNames.AniList);
|
||||
return GetImages(seriesId, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RemoteImageInfo>> GetImages(string aid, CancellationToken cancellationToken)
|
||||
{
|
||||
var list = new List<RemoteImageInfo>();
|
||||
|
||||
if (!string.IsNullOrEmpty(aid))
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_appPaths = appPaths;
|
||||
}
|
||||
|
||||
public string Name => "AniList";
|
||||
|
||||
public bool Supports(BaseItem item) => item is Series || item is Season;
|
||||
|
||||
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
|
||||
{
|
||||
return new[] { ImageType.Primary, ImageType.Banner };
|
||||
}
|
||||
|
||||
public Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
|
||||
{
|
||||
var seriesId = item.GetProviderId(ProviderNames.AniList);
|
||||
return GetImages(seriesId, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RemoteImageInfo>> GetImages(string aid, CancellationToken cancellationToken)
|
||||
{
|
||||
var list = new List<RemoteImageInfo>();
|
||||
|
||||
if (!string.IsNullOrEmpty(aid))
|
||||
var primary = _api.Get_ImageUrl(await _api.WebRequestAPI(_api.AniList_anime_link.Replace("{0}", aid)));
|
||||
list.Add(new RemoteImageInfo
|
||||
{
|
||||
var primary = await GetSeriesImage(_appPaths, aid, "image");
|
||||
if (!string.IsNullOrEmpty(primary))
|
||||
{
|
||||
list.Add(new RemoteImageInfo
|
||||
{
|
||||
ProviderName = Name,
|
||||
Type = ImageType.Primary,
|
||||
Url = primary
|
||||
});
|
||||
}
|
||||
|
||||
var banner = await GetSeriesImage(_appPaths, aid, "banner");
|
||||
if (!string.IsNullOrEmpty(banner))
|
||||
{
|
||||
list.Add(new RemoteImageInfo
|
||||
{
|
||||
ProviderName = Name,
|
||||
Type = ImageType.Banner,
|
||||
Url = banner
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
return _httpClient.GetResponse(new HttpRequestOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
Url = url,
|
||||
ResourcePool = ResourcePool
|
||||
ProviderName = Name,
|
||||
Type = ImageType.Primary,
|
||||
Url = primary
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
return _httpClient.GetResponse(new HttpRequestOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
Url = url,
|
||||
ResourcePool = AniListSeriesProvider.ResourcePool
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +1,169 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
{
|
||||
public class Anime
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string description { get; set; }
|
||||
public string title_romaji { get; set; }
|
||||
public string title_japanese { get; set; }
|
||||
public string title_english { get; set; }
|
||||
public string start_date { get; set; }
|
||||
public string end_date { get; set; }
|
||||
public string image_url_lge { get; set; }
|
||||
public string image_url_banner { get; set; }
|
||||
public List<string> genres { get; set; }
|
||||
public int? duration { get; set; }
|
||||
public string airing_status { get; set; }
|
||||
public class Title
|
||||
{
|
||||
public string romaji { get; set; }
|
||||
public string english { get; set; }
|
||||
public string native { get; set; }
|
||||
}
|
||||
|
||||
public class CoverImage
|
||||
{
|
||||
public string medium { get; set; }
|
||||
public string large { get; set; }
|
||||
}
|
||||
|
||||
public class StartDate
|
||||
{
|
||||
public int year { get; set; }
|
||||
public int month { get; set; }
|
||||
public int day { get; set; }
|
||||
}
|
||||
|
||||
public class EndDate
|
||||
{
|
||||
public int year { get; set; }
|
||||
public int month { get; set; }
|
||||
public int day { get; set; }
|
||||
}
|
||||
|
||||
public class Medium
|
||||
{
|
||||
public int id { get; set; }
|
||||
public Title title { get; set; }
|
||||
public CoverImage coverImage { get; set; }
|
||||
public string format { get; set; }
|
||||
public string type { get; set; }
|
||||
public int averageScore { get; set; }
|
||||
public int popularity { get; set; }
|
||||
public int episodes { get; set; }
|
||||
public string season { get; set; }
|
||||
public string hashtag { get; set; }
|
||||
public bool isAdult { get; set; }
|
||||
public StartDate startDate { get; set; }
|
||||
public EndDate endDate { get; set; }
|
||||
public object bannerImage { get; set; }
|
||||
public string status { get; set; }
|
||||
public object chapters { get; set; }
|
||||
public object volumes { get; set; }
|
||||
public string description { get; set; }
|
||||
public int meanScore { get; set; }
|
||||
public List<string> genres { get; set; }
|
||||
public List<object> synonyms { get; set; }
|
||||
public object nextAiringEpisode { get; set; }
|
||||
}
|
||||
|
||||
public class Page
|
||||
{
|
||||
public List<Medium> media { get; set; }
|
||||
}
|
||||
|
||||
public class Data
|
||||
{
|
||||
public Page Page { get; set; }
|
||||
public Media Media { get; set; }
|
||||
}
|
||||
|
||||
public class Media
|
||||
{
|
||||
public Characters characters { get; set; }
|
||||
public int popularity { get; set; }
|
||||
public object hashtag { get; set; }
|
||||
public bool isAdult { get; set; }
|
||||
public int id { get; set; }
|
||||
public Title title { get; set; }
|
||||
public StartDate startDate { get; set; }
|
||||
public EndDate endDate { get; set; }
|
||||
public CoverImage coverImage { get; set; }
|
||||
public object bannerImage { get; set; }
|
||||
public string format { get; set; }
|
||||
public string type { get; set; }
|
||||
public string status { get; set; }
|
||||
public int episodes { get; set; }
|
||||
public object chapters { get; set; }
|
||||
public object volumes { get; set; }
|
||||
public string season { get; set; }
|
||||
public string description { get; set; }
|
||||
public int averageScore { get; set; }
|
||||
public int meanScore { get; set; }
|
||||
public List<string> genres { get; set; }
|
||||
public List<object> synonyms { get; set; }
|
||||
public object nextAiringEpisode { get; set; }
|
||||
}
|
||||
public class PageInfo
|
||||
{
|
||||
public int total { get; set; }
|
||||
public int perPage { get; set; }
|
||||
public bool hasNextPage { get; set; }
|
||||
public int currentPage { get; set; }
|
||||
public int lastPage { get; set; }
|
||||
}
|
||||
|
||||
public class Name
|
||||
{
|
||||
public string first { get; set; }
|
||||
public string last { get; set; }
|
||||
}
|
||||
|
||||
public class Image
|
||||
{
|
||||
public string medium { get; set; }
|
||||
public string large { get; set; }
|
||||
}
|
||||
|
||||
public class Node
|
||||
{
|
||||
public int id { get; set; }
|
||||
public Name name { get; set; }
|
||||
public Image image { get; set; }
|
||||
}
|
||||
|
||||
public class Name2
|
||||
{
|
||||
public string first { get; set; }
|
||||
public string last { get; set; }
|
||||
public string native { get; set; }
|
||||
}
|
||||
|
||||
public class Image2
|
||||
{
|
||||
public string medium { get; set; }
|
||||
public string large { get; set; }
|
||||
}
|
||||
|
||||
public class VoiceActor
|
||||
{
|
||||
public int id { get; set; }
|
||||
public Name2 name { get; set; }
|
||||
public Image2 image { get; set; }
|
||||
public string language { get; set; }
|
||||
}
|
||||
|
||||
public class Edge
|
||||
{
|
||||
public Node node { get; set; }
|
||||
public string role { get; set; }
|
||||
public List<VoiceActor> voiceActors { get; set; }
|
||||
}
|
||||
|
||||
public class Characters
|
||||
{
|
||||
public PageInfo pageInfo { get; set; }
|
||||
public List<Edge> edges { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class RootObject
|
||||
{
|
||||
public Data data { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
415
MediaBrowser.Plugins.Anime/Providers/AniList/api.cs
Normal file
415
MediaBrowser.Plugins.Anime/Providers/AniList/api.cs
Normal file
@ -0,0 +1,415 @@
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Plugins.Anime.Configuration;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Plugins.Anime.Providers.AniList.MediaBrowser.Plugins.Anime.Providers.AniList;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
||||
namespace MediaBrowser.Plugins.Anime.Providers.AniList
|
||||
{
|
||||
/// <summary>
|
||||
/// Based on the new API from AniList
|
||||
/// 🛈 This code works with the API Interface (v2) from AniList
|
||||
/// 🛈 https://anilist.gitbooks.io/anilist-apiv2-docs
|
||||
/// 🛈 THIS IS AN UNOFFICAL API INTERFACE FOR EMBY
|
||||
/// </summary>
|
||||
public class api
|
||||
{
|
||||
private static IJsonSerializer _jsonSerializer;
|
||||
public List<string> anime_search_names = new List<string>();
|
||||
public List<string> anime_search_ids = new List<string>();
|
||||
public string SearchLink = @"https://graphql.anilist.co/api/v2?query=
|
||||
query ($query: String, $type: MediaType) {
|
||||
Page {
|
||||
media(search: $query, type: $type) {
|
||||
id
|
||||
title {
|
||||
romaji
|
||||
english
|
||||
native
|
||||
}
|
||||
coverImage {
|
||||
medium
|
||||
large
|
||||
}
|
||||
format
|
||||
type
|
||||
averageScore
|
||||
popularity
|
||||
episodes
|
||||
season
|
||||
hashtag
|
||||
isAdult
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
endDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
}
|
||||
}
|
||||
}&variables={ ""query"":""{0}"",""type"":""ANIME""}";
|
||||
public string AniList_anime_link = @"https://graphql.anilist.co/api/v2?query=query($id: Int!, $type: MediaType) {
|
||||
Media(id: $id, type: $type)
|
||||
{
|
||||
id
|
||||
title {
|
||||
romaji
|
||||
english
|
||||
native
|
||||
userPreferred
|
||||
}
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
endDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
coverImage {
|
||||
large
|
||||
medium
|
||||
}
|
||||
bannerImage
|
||||
format
|
||||
type
|
||||
status
|
||||
episodes
|
||||
chapters
|
||||
volumes
|
||||
season
|
||||
description
|
||||
averageScore
|
||||
meanScore
|
||||
genres
|
||||
synonyms
|
||||
nextAiringEpisode {
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
}
|
||||
}&variables={ ""id"":""{0}"",""type"":""ANIME""}";
|
||||
public string AniList_anime_char_link = @"https://graphql.anilist.co/api/v2?query=query($id: Int!, $type: MediaType, $page: Int = 1) {
|
||||
Media(id: $id, type: $type) {
|
||||
id
|
||||
characters(page: $page, sort: [ROLE]) {
|
||||
pageInfo {
|
||||
total
|
||||
perPage
|
||||
hasNextPage
|
||||
currentPage
|
||||
lastPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name {
|
||||
first
|
||||
last
|
||||
}
|
||||
image {
|
||||
medium
|
||||
large
|
||||
}
|
||||
}
|
||||
role
|
||||
voiceActors {
|
||||
id
|
||||
name {
|
||||
first
|
||||
last
|
||||
native
|
||||
}
|
||||
image {
|
||||
medium
|
||||
large
|
||||
}
|
||||
language
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}&variables={ ""id"":""{0}"",""type"":""ANIME""}";
|
||||
public api(IJsonSerializer jsonSerializer)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
}
|
||||
/// <summary>
|
||||
/// API call to get the anime with the id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<RemoteSearchResult> GetAnime(string id)
|
||||
{
|
||||
RootObject WebContent = await WebRequestAPI(AniList_anime_link.Replace("{0}",id));
|
||||
|
||||
var result = new RemoteSearchResult
|
||||
{
|
||||
Name = ""
|
||||
};
|
||||
|
||||
result.SearchProviderName = WebContent.data.Media.title.romaji;
|
||||
result.ImageUrl = WebContent.data.Media.coverImage.large;
|
||||
result.SetProviderId(ProviderNames.AniList, id);
|
||||
result.Overview = WebContent.data.Media.description;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API call to select the lang
|
||||
/// </summary>
|
||||
/// <param name="WebContent"></param>
|
||||
/// <param name="preference"></param>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
private string SelectName(RootObject WebContent, TitlePreferenceType preference, string language)
|
||||
{
|
||||
if (preference == TitlePreferenceType.Localized && language == "en")
|
||||
return WebContent.data.Media.title.english;
|
||||
if (preference == TitlePreferenceType.Japanese)
|
||||
return WebContent.data.Media.title.native;
|
||||
|
||||
return WebContent.data.Media.title.romaji;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API call to get the title with the right lang
|
||||
/// </summary>
|
||||
/// <param name="lang"></param>
|
||||
/// <param name="WebContent"></param>
|
||||
/// <returns></returns>
|
||||
public string Get_title(string lang, RootObject WebContent)
|
||||
{
|
||||
switch (lang)
|
||||
{
|
||||
case "en":
|
||||
return WebContent.data.Media.title.english;
|
||||
|
||||
case "jap":
|
||||
return WebContent.data.Media.title.native;
|
||||
|
||||
//Default is jap_r
|
||||
default:
|
||||
return WebContent.data.Media.title.romaji;
|
||||
}
|
||||
}
|
||||
public async Task<List<PersonInfo>> getPersonInfo(int id)
|
||||
{
|
||||
List<PersonInfo> lpi = new List<PersonInfo>();
|
||||
RootObject WebContent = await WebRequestAPI(AniList_anime_char_link.Replace("{0}", id.ToString()));
|
||||
foreach (Edge edge in WebContent.data.Media.characters.edges)
|
||||
{
|
||||
PersonInfo pi = new PersonInfo();
|
||||
pi.Name = edge.node.name.first+" "+ edge.node.name.last;
|
||||
pi.ItemId = ToGuid(edge.node.id);
|
||||
pi.ImageUrl = edge.node.image.large;
|
||||
pi.Role = edge.role;
|
||||
}
|
||||
return lpi;
|
||||
}
|
||||
/// <summary>
|
||||
/// Convert int to Guid
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static Guid ToGuid(int value)
|
||||
{
|
||||
byte[] bytes = new byte[16];
|
||||
BitConverter.GetBytes(value).CopyTo(bytes, 0);
|
||||
return new Guid(bytes);
|
||||
}
|
||||
/// <summary>
|
||||
/// API call to get the genre of the anime
|
||||
/// </summary>
|
||||
/// <param name="WebContent"></param>
|
||||
/// <returns></returns>
|
||||
public List<string> Get_Genre(RootObject WebContent)
|
||||
{
|
||||
|
||||
return WebContent.data.Media.genres;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API call to get the img url
|
||||
/// </summary>
|
||||
/// <param name="WebContent"></param>
|
||||
/// <returns></returns>
|
||||
public string Get_ImageUrl(RootObject WebContent)
|
||||
{
|
||||
return WebContent.data.Media.coverImage.large;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API call too get the rating
|
||||
/// </summary>
|
||||
/// <param name="WebContent"></param>
|
||||
/// <returns></returns>
|
||||
public string Get_Rating(RootObject WebContent)
|
||||
{
|
||||
return (WebContent.data.Media.averageScore / 10).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API call to get the description
|
||||
/// </summary>
|
||||
/// <param name="WebContent"></param>
|
||||
/// <returns></returns>
|
||||
public string Get_Overview(RootObject WebContent)
|
||||
{
|
||||
return WebContent.data.Media.description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API call to search a title and return the right one back
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> Search_GetSeries(string title, CancellationToken cancellationToken)
|
||||
{
|
||||
anime_search_names.Clear();
|
||||
anime_search_ids.Clear();
|
||||
string result = null;
|
||||
RootObject WebContent = await WebRequestAPI(SearchLink.Replace("{0}", title));
|
||||
foreach (Medium media in WebContent.data.Page.media) {
|
||||
//get id
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (await Task.Run(() => Equals_check.Compare_strings(media.title.romaji, title)))
|
||||
{
|
||||
return media.id.ToString();
|
||||
}
|
||||
if (await Task.Run(() => Equals_check.Compare_strings(media.title.english, title)))
|
||||
{
|
||||
return media.id.ToString();
|
||||
}
|
||||
//Disabled due to false result.
|
||||
/*if (await Task.Run(() => Equals_check.Compare_strings(media.title.native, title)))
|
||||
{
|
||||
return media.id.ToString();
|
||||
}*/
|
||||
int n;
|
||||
if (Int32.TryParse(media.id.ToString(), out n))
|
||||
{
|
||||
anime_search_names.Add(media.title.romaji);
|
||||
anime_search_ids.Add(media.id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API call to search a title and return a list back
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<string>> Search_GetSeries_list(string title, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
RootObject WebContent = await WebRequestAPI(SearchLink.Replace("{0}", title));
|
||||
foreach (Medium media in WebContent.data.Page.media)
|
||||
{
|
||||
//get id
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (await Task.Run(() => Equals_check.Compare_strings(media.title.romaji, title)))
|
||||
{
|
||||
result.Add(media.id.ToString());
|
||||
}
|
||||
if (await Task.Run(() => Equals_check.Compare_strings(media.title.english, title)))
|
||||
{
|
||||
result.Add(media.id.ToString());
|
||||
}
|
||||
//Disabled due to false result.
|
||||
/*if (await Task.Run(() => Equals_check.Compare_strings(media.title.native, title)))
|
||||
{
|
||||
result.Add(media.id.ToString());
|
||||
}*/
|
||||
}
|
||||
|
||||
catch (Exception) { }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SEARCH Title
|
||||
/// </summary>
|
||||
public async Task<string> FindSeries(string title, CancellationToken cancellationToken)
|
||||
{
|
||||
string aid = await Search_GetSeries(title, cancellationToken);
|
||||
if (!string.IsNullOrEmpty(aid))
|
||||
{
|
||||
return aid;
|
||||
}
|
||||
aid = await Search_GetSeries(Equals_check.clear_name(title), cancellationToken);
|
||||
if (!string.IsNullOrEmpty(aid))
|
||||
{
|
||||
return aid;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple regex
|
||||
/// </summary>
|
||||
public async Task<string> One_line_regex(Regex regex, string match, int group = 1, int match_int = 0)
|
||||
{
|
||||
Regex _regex = regex;
|
||||
int x = 0;
|
||||
MatchCollection matches = await Task.Run(() => regex.Matches(match));
|
||||
foreach (Match _match in matches)
|
||||
{
|
||||
if (x == match_int)
|
||||
{
|
||||
return await Task.Run(() => _match.Groups[group].Value.ToString());
|
||||
}
|
||||
x++;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET website content from the link
|
||||
/// </summary>
|
||||
public async Task<RootObject> WebRequestAPI(string link)
|
||||
{
|
||||
string _strContent = "";
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
var values = new System.Collections.Specialized.NameValueCollection();
|
||||
|
||||
var response = await Task.Run(() => client.UploadValues(new Uri(link),values));
|
||||
_strContent = System.Text.Encoding.Default.GetString(response);
|
||||
}
|
||||
|
||||
RootObject data = _jsonSerializer.DeserializeFromString<RootObject>(_strContent);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
@ -95,7 +95,7 @@ namespace MediaBrowser.Plugins.Anime.Providers
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Cut p(%) away from the string
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user