Cleaned up and refactored web request API

This commit is contained in:
Alexander Rufenach
2021-06-04 01:28:55 -04:00
parent e103be43a9
commit da91e6e2c7
3 changed files with 160 additions and 125 deletions
+112 -95
View File
@@ -24,19 +24,20 @@ namespace Jellyfin.Plugin.AnilistSync.API
private readonly IHttpClientFactory _httpClientFactory;
private readonly JsonSerializerOptions _jsonSerializerOptions;
private const string listIdQuery = @"mutation ($mediaId: Int) {SaveMediaListEntry(mediaId: $mediaId) {id, status, repeat, progress}}&variables={""mediaId"": ""{0}""}";
private const string listUpdateQuery = @"mutation ($id: Int, $progress: Int, $status: MediaListStatus, $repeat: Int) {SaveMediaListEntry(id: $id, progress: $progress, status: $status, repeat: $repeat) {id, progress, status, repeat}}";
private const string listStatusUpdateQuery = @"mutation ($id: Int, $status: MediaListStatus) {SaveMediaListEntry(id: $id, status: $status) {id, 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}"", ""status"":""{1}""}";
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 GraphQLUrl = @"https://graphql.anilist.co";
public const string QueryString = @"
mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $progress: Int,) {
SaveMediaListEntry(id: $id, mediaId: $mediaId, status: $status, progress: $progress ) {
id,
mediaId,
status,
progress
}
}";
public const int ClientId = 5659;
public const string Secret = @"h7ym2GZ6OjrdJ9sygDP7kDnQWsBdTwp4U8s7pt4X";
public AnilistApi(ILogger<AnilistApi> logger, IHttpClientFactory httpClientFactory)
@@ -49,112 +50,128 @@ namespace Jellyfin.Plugin.AnilistSync.API
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");
string uri = @"/oauth/token";
string payload = JsonSerializer.Serialize(new OAuth
{
GrantType = "authorization_code",
ClientId = ClientId,
ClientSecret = Secret,
RedirectUri = $"{ BaseOauthUrl }/oauth/pin",
Code = code
});
using 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?> GetEpisodes(int? anilistId)
{
GraphQLBody content = new GraphQLBody {
Query = @"query ($mediaId: Int) { Media (id: $mediaId) { episodes }}",
Variables = new ListEntry
{
MediaId = anilistId,
Id = null,
Progress = null,
Status = null
}
};
return await Post(content);
}
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;
GraphQLBody content = new GraphQLBody {
Query = @"query { Viewer { id, name }}",
Variables = null
};
return await PostWithAuth(userToken, content);
}
public async Task<RootObject?> GetListEntry(string anilistId, string? userToken)
public async Task<RootObject?> GetListEntry(string? userToken, int? anilistId)
{
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)
GraphQLBody content = new GraphQLBody
{
throw new AnilistAPIException(data.Errors);
}
return data;
Query = QueryString,
Variables = new ListEntry
{
MediaId = anilistId,
Id = null,
Progress = null,
Status = null
}
};
return await PostWithAuth(userToken, content);
}
public async Task<RootObject?> GetEpisodes(string anilistId)
public async Task<RootObject?> PostListStatusUpdate(string? userToken, int? listId, MediaListStatus? status)
{
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)
GraphQLBody content = new GraphQLBody
{
throw new AnilistAPIException(data.Errors);
}
return data;
Query = QueryString,
Variables = new ListEntry
{
MediaId = null,
Id = listId,
Progress = null,
Status = status
}
};
return await PostWithAuth(userToken, content);
}
public async Task<RootObject?> PostListStatusUpdate(string? anilistMediaId, string? userToken, MediaListStatus? status)
public async Task<RootObject?> PostListProgressUpdate(string? userToken, int? listId, int? progress)
{
var requestMessage = new HttpRequestMessage();
requestMessage.RequestUri = new Uri(
BaseGraphQLUrl +
listStatusUpdateQuery +
listUpdateVars2
.Replace("{0}", anilistMediaId)
.Replace("{1}", 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)
GraphQLBody content = new GraphQLBody
{
throw new AnilistAPIException(data.Errors);
}
return data;
Query = QueryString,
Variables = new ListEntry
{
MediaId = null,
Id = listId,
Progress = progress,
Status = null
}
};
return await PostWithAuth(userToken, content);
}
public async Task<RootObject?> PostListProgressUpdate(string? anilistMediaId, string? userToken, int? progress)
public async Task<RootObject?> Post(GraphQLBody graphQL)
{
var requestMessage = new HttpRequestMessage();
requestMessage.RequestUri = new Uri(
BaseGraphQLUrl +
listUpdateQuery +
listUpdateVars1
.Replace("{0}", anilistMediaId)
.Replace("{1}", progress.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)
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).PostAsJsonAsync<GraphQLBody>(GraphQLUrl, graphQL, _jsonSerializerOptions);
var root = await responseMessage.Content.ReadFromJsonAsync<RootObject>(_jsonSerializerOptions);
if (root?.Errors != null)
{
throw new AnilistAPIException(data.Errors);
foreach (AnilistError error in root.Errors)
{
throw new AnilistAPIException(error.ErrorMessage, error.ErrorStatus, error.Locations);
}
}
return data;
return root;
}
public async Task<RootObject?> PostWithAuth(string? userToken, GraphQLBody graphQL)
{
using var requestMessage = new HttpRequestMessage
{
RequestUri = new Uri(GraphQLUrl),
Method = HttpMethod.Post
};
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", userToken);
string content = JsonSerializer.Serialize(graphQL, _jsonSerializerOptions);
requestMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(requestMessage);
var root = await responseMessage.Content.ReadFromJsonAsync<RootObject>(_jsonSerializerOptions);
if (root?.Errors != null)
{
foreach (AnilistError error in root.Errors)
{
throw new AnilistAPIException(error.ErrorMessage, error.ErrorStatus, error.Locations);
}
}
return root;
}
}
}
+38 -10
View File
@@ -10,7 +10,7 @@ namespace Jellyfin.Plugin.AnilistSync.API
public Data? Data { get; set; }
[JsonPropertyName("errors")]
public Error[]? Errors { get; set; }
public AnilistError[]? Errors { get; set; }
}
public class Data
@@ -25,7 +25,7 @@ namespace Jellyfin.Plugin.AnilistSync.API
public Media? Media { get; set; }
}
public class Error
public class AnilistError
{
[JsonPropertyName("message")]
public string? ErrorMessage { get; set; }
@@ -63,6 +63,9 @@ namespace Jellyfin.Plugin.AnilistSync.API
public class ListEntry
{
[JsonPropertyName("mediaId")]
public int? MediaId { get; set; }
[JsonPropertyName("id")]
public int? Id { get; set; }
@@ -71,9 +74,33 @@ namespace Jellyfin.Plugin.AnilistSync.API
[JsonPropertyName("status")]
public MediaListStatus? Status { get; set; }
}
[JsonPropertyName("repeat")]
public int? TimesRewatched { get; set; }
public class GraphQLBody
{
[JsonPropertyName("query")]
public string? Query { get; set; }
[JsonPropertyName("variables")]
public ListEntry? Variables { get; set; }
}
public class OAuth
{
[JsonPropertyName("grant_type")]
public string? GrantType { get; set; }
[JsonPropertyName("client_id")]
public int? ClientId { get; set; }
[JsonPropertyName("client_secret")]
public string? ClientSecret { get; set; }
[JsonPropertyName("redirect_uri")]
public string? RedirectUri { get; set; }
[JsonPropertyName("code")]
public string? Code { get; set; }
}
public enum MediaListStatus
@@ -99,14 +126,13 @@ namespace Jellyfin.Plugin.AnilistSync.API
[JsonPropertyName("refresh_token")]
public string? RefreshToken { get; set; }
}
public class AnilistAPIException : Exception
{
public Error[]? errors;
public int? statusCode;
public Location[]? locations;
public AnilistAPIException()
{
@@ -120,14 +146,16 @@ namespace Jellyfin.Plugin.AnilistSync.API
}
public AnilistAPIException(string message, Exception inner)
: base(message, inner)
: base(message, inner)
{
}
public AnilistAPIException(Error[] errors)
public AnilistAPIException(string? message, int? statusCode, Location[]? locations)
: base(message)
{
this.errors = errors;
this.statusCode = statusCode;
this.locations = locations;
}
}
}