Adding additional files
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Net.Mime;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Plugin.AnilistSync.API
|
||||
{
|
||||
public class AnilistApi
|
||||
{
|
||||
private readonly ILogger<AnilistApi> _logger;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
private const string listIdQuery = @"mutation ($mediaId: Int) {SaveMediaListEntry(mediaId: $mediaId) {id, status}}&variables={""mediaId"": ""{0}""}";
|
||||
private const string listUpdateQuery = @"mutation ($id: Int, $progress: Int, $status: MediaListStatus) {SaveMediaListEntry(id: $id, progress: $progress, status: $status) {id, progress, status}}";
|
||||
private const string episodeQuery = @"query ($id: Int) {Media (id: $id) {episodes}}";
|
||||
private const string currentUserQuery = @"query {Viewer {id, name}}";
|
||||
|
||||
private const string listUpdateVars1 = @"&variables={""id"":""{0}"", ""progress"":""{1}""}";
|
||||
private const string listUpdateVars2 = @"&variables={""id"":""{0}"", ""progress"":""{1}"", ""status"":""{2}""}";
|
||||
|
||||
public const string BaseOauthUrl = @"https://anilist.co/api/v2";
|
||||
public const string BaseGraphQLUrl = @"https://graphql.anilist.co/api/v2?query=";
|
||||
public const string RedirectUri = BaseOauthUrl + @"/oauth/pin";
|
||||
public const string ClientId = @"5659";
|
||||
public const string Secret = @"h7ym2GZ6OjrdJ9sygDP7kDnQWsBdTwp4U8s7pt4X";
|
||||
|
||||
public AnilistApi(ILogger<AnilistApi> logger, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_jsonSerializerOptions = JsonDefaults.GetOptions();
|
||||
}
|
||||
|
||||
|
||||
public async Task<CodeResponse?> GetToken(string? code)
|
||||
{
|
||||
var uri = $"/oauth/token";
|
||||
|
||||
var payload = $"{{\"grant_type\": \"authorization_code\",\"client_id\": {ClientId}, \"client_secret\": \"{Secret}\", \"redirect_uri\": \"{BaseOauthUrl}/oauth/pin\",\"code\": \"{code}\"}}";
|
||||
HttpContent content = new StringContent(payload, Encoding.UTF8, "application/json");
|
||||
|
||||
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).PostAsync(BaseOauthUrl + uri, content);
|
||||
return await responseMessage.Content.ReadFromJsonAsync<CodeResponse>(_jsonSerializerOptions);
|
||||
}
|
||||
|
||||
public async Task<RootObject?> GetUser(string? userToken)
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage();
|
||||
requestMessage.RequestUri = new Uri(BaseGraphQLUrl + currentUserQuery);
|
||||
requestMessage.Method = HttpMethod.Post;
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", userToken);
|
||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
requestMessage.Content = new StringContent("", Encoding.UTF8, "application/json");
|
||||
|
||||
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(requestMessage);
|
||||
|
||||
var data = await responseMessage.Content.ReadFromJsonAsync<RootObject>(_jsonSerializerOptions);
|
||||
if (data?.Errors != null)
|
||||
{
|
||||
throw new AnilistAPIException(data.Errors);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task<RootObject?> GetListId(string anilistId, string? userToken)
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage();
|
||||
requestMessage.RequestUri = new Uri(BaseGraphQLUrl + listIdQuery.Replace("{0}", anilistId));
|
||||
requestMessage.Method = HttpMethod.Post;
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", userToken);
|
||||
requestMessage.Content = new StringContent("", Encoding.UTF8, "application/json");
|
||||
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(requestMessage);
|
||||
var data = await responseMessage.Content.ReadFromJsonAsync<RootObject>(_jsonSerializerOptions);
|
||||
if (data?.Errors != null)
|
||||
{
|
||||
throw new AnilistAPIException(data.Errors);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task<RootObject?> GetEpisodes(string anilistId)
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage();
|
||||
requestMessage.RequestUri = new Uri(BaseGraphQLUrl + episodeQuery + $"&variables={{\"id\":{anilistId}}}");
|
||||
requestMessage.Method = HttpMethod.Post;
|
||||
requestMessage.Content = new StringContent("", Encoding.UTF8, "application/json");
|
||||
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(requestMessage);
|
||||
var data = await responseMessage.Content.ReadFromJsonAsync<RootObject>(_jsonSerializerOptions);
|
||||
if (data?.Errors != null)
|
||||
{
|
||||
throw new AnilistAPIException(data.Errors);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task<RootObject?> PostListUpdate(string anilistId, string? userToken, int? progress, MediaListStatus status)
|
||||
{
|
||||
var listEntry = GetListId(anilistId, userToken).Result?.Data?.ListEntry;
|
||||
|
||||
var requestMessage = new HttpRequestMessage();
|
||||
requestMessage.RequestUri = new Uri(BaseGraphQLUrl + listUpdateQuery + listUpdateVars2.Replace("{0}", listEntry?.Id.ToString()).Replace("{1}", progress.ToString()).Replace("{2}", status.ToString()));
|
||||
requestMessage.Method = HttpMethod.Post;
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", userToken);
|
||||
requestMessage.Content = new StringContent("", Encoding.UTF8, "application/json");
|
||||
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(requestMessage);
|
||||
var data = await responseMessage.Content.ReadFromJsonAsync<RootObject>(_jsonSerializerOptions);
|
||||
if (data?.Errors != null)
|
||||
{
|
||||
throw new AnilistAPIException(data.Errors);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Jellyfin.Plugin.AnilistSync.API
|
||||
{
|
||||
public class RootObject
|
||||
{
|
||||
[JsonPropertyName("data")]
|
||||
public Data? Data { get; set; }
|
||||
|
||||
[JsonPropertyName("errors")]
|
||||
public Error[]? Errors { get; set; }
|
||||
}
|
||||
|
||||
public class Data
|
||||
{
|
||||
[JsonPropertyName("Viewer")]
|
||||
public User? User { get; set; }
|
||||
|
||||
[JsonPropertyName("SaveMediaListEntry")]
|
||||
public ListEntry? ListEntry { get; set; }
|
||||
|
||||
[JsonPropertyName("Media")]
|
||||
public Media? Media { get; set; }
|
||||
}
|
||||
|
||||
public class Error
|
||||
{
|
||||
[JsonPropertyName("message")]
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public int? ErrorStatus { get; set; }
|
||||
|
||||
[JsonPropertyName("locations")]
|
||||
public Location[]? Locations { get; set; }
|
||||
}
|
||||
|
||||
public class Location
|
||||
{
|
||||
[JsonPropertyName("line")]
|
||||
public int? Line { get; set; }
|
||||
|
||||
[JsonPropertyName("column")]
|
||||
public int? column { get; set; }
|
||||
}
|
||||
|
||||
public class Media
|
||||
{
|
||||
[JsonPropertyName("episodes")]
|
||||
public int? Episodes { get; set; }
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public int? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
|
||||
public class ListEntry
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public int? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("progress")]
|
||||
public int? Progress { get; set; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public MediaListStatus? Status { get; set; }
|
||||
}
|
||||
|
||||
public enum MediaListStatus
|
||||
{
|
||||
CURRENT,
|
||||
PLANNING,
|
||||
COMPLETED,
|
||||
DROPPED,
|
||||
PAUSED,
|
||||
REPEATING
|
||||
}
|
||||
|
||||
public class CodeResponse
|
||||
{
|
||||
[JsonPropertyName("token_type")]
|
||||
public string? TokenType { get; set; }
|
||||
|
||||
[JsonPropertyName("expires_in")]
|
||||
public int? ExpiresIn { get; set; }
|
||||
|
||||
[JsonPropertyName("access_token")]
|
||||
public string? AccessToken { get; set; }
|
||||
|
||||
[JsonPropertyName("refresh_token")]
|
||||
public string? RefreshToken { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class AnilistAPIException : Exception
|
||||
{
|
||||
public Error[]? errors;
|
||||
|
||||
public AnilistAPIException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AnilistAPIException(string message)
|
||||
: base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AnilistAPIException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AnilistAPIException(Error[] errors)
|
||||
{
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Jellyfin.Plugin.AnilistSync.API
|
||||
{
|
||||
[ApiController]
|
||||
[Authorize(Policy = "DefaultAuthorization")]
|
||||
[Route("AnilistSync")]
|
||||
public class Endpoints : ControllerBase
|
||||
{
|
||||
private readonly AnilistApi _anilistApi;
|
||||
|
||||
public Endpoints(AnilistApi anilistApi)
|
||||
{
|
||||
_anilistApi = anilistApi;
|
||||
}
|
||||
|
||||
[HttpGet("oauth/token/{userCode}")]
|
||||
public async Task<ActionResult<CodeResponse?>> GetToken([FromRoute] string userCode)
|
||||
{
|
||||
return await _anilistApi.GetToken(userCode);
|
||||
}
|
||||
|
||||
[HttpGet("users/settings/{userId}")]
|
||||
public async Task<ActionResult<RootObject?>> GetUser([FromRoute] Guid userId)
|
||||
{
|
||||
var userConfiguration = Plugin.Instance?.Configuration.GetByGuid(userId);
|
||||
if (userConfiguration == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return await _anilistApi.GetUser(userConfiguration.UserToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user