Working on parsing the json file for mal import

This commit is contained in:
iceypotato 2023-07-25 11:46:28 -07:00
parent 825e136443
commit 87a323d3c5
5 changed files with 221 additions and 13 deletions

View File

@ -0,0 +1,31 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.ImportLists.MyAnimeList
{
public class MalResponse
{
[JsonProperty("data")]
public List<MalAnime> Animes { get; set; }
}
public class MalAnime
{
[JsonProperty("node")]
public MalAnimeInfo AnimeInfo { get; set; }
[JsonProperty("list_status")]
public MalAnimeListStatus ListStatus { get; set; }
}
public class MalAnimeInfo
{
public int Id { get; set; }
public string Title { get; set; }
}
public class MalAnimeListStatus
{
public string Status { get; set; }
}
}

View File

@ -1,15 +1,31 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Cryptography;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using HttpClient = System.Net.Http.HttpClient;
namespace NzbDrone.Core.ImportLists.MyAnimeList
{
internal class MalImport : HttpImportListBase<MalListSettings>
public class MalImport : HttpImportListBase<MalListSettings>
{
public const string OAuthUrl = "https://myanimelist.net/v1/oauth2/authorize";
public const string OAuthTokenUrl = "https://myanimelist.net/v1/oauth2/token";
public const string ClientId = "4d057302cee543511828cac37864235a";
public const string ClientSecret = "756e293edefff82edf6245fb02e23aacfa58c602e4c49f51acf5aa9161685f6f";
public const string RedirectUri = "http://localhost:8989/oauth.html";
public static string MalIdConversions = "";
public static Dictionary<int, int> Maltotvdb = new Dictionary<int, int>();
private string _Codechallenge = "";
public override string Name => "MyAnimeList";
public override ImportListType ListType => ImportListType.Other;
public override TimeSpan MinRefreshInterval => TimeSpan.FromSeconds(10); // change this later
@ -18,13 +34,61 @@ namespace NzbDrone.Core.ImportLists.MyAnimeList
public MalImport(IHttpClient httpClient, IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
: base(httpClient, importListStatusService, configService, parsingService, logger)
{
_Codechallenge = GeneratePKCEforMal();
LoadMalIDConversionJson();
}
// This method should refresh (dunno what that means) the token
// This method also fetches the anime from mal?
public override IList<ImportListItemInfo> Fetch()
{
//_importListStatusService;
return FetchItems(g => g.GetListItems());
}
// This method is used for generating the access token.
// In the MAL API instructions (https://myanimelist.net/blog.php?eid=835707)
// we need to create an app entry for this.
// This doesn't work. 2 requests need to be made, one to get the auth code, then the second to get the json response
public override object RequestAction(string action, IDictionary<string, string> query)
{
if (action == "startOAuth")
{
// The workaround
// Have mal redirect, then make copy and paste the returned stuff into a text box for sonarr to use.
// Create those boxes in MalListSettings
var request = new HttpRequestBuilder(OAuthUrl)
.AddQueryParam("response_type", "code")
.AddQueryParam("client_id", ClientId)
.AddQueryParam("code_challenge", _Codechallenge)
.AddQueryParam("state", query["callbackUrl"])
.AddQueryParam("redirect_uri", "http://localhost:8989/api/v3/importlist/action/ligma")
.Build();
return new
{
OauthUrl = request.Url.ToString()
};
}
else if (action == "getOAuthToken")
{
var request = new HttpRequestBuilder(OAuthTokenUrl)
.AddQueryParam("response_type", "code")
.AddQueryParam("client_id", ClientId)
.AddQueryParam("client_secret", ClientSecret)
.AddQueryParam("code", query["code"])
.AddQueryParam("code_challenge", _Codechallenge)
.AddQueryParam("grant_type", "authorization_code")
//.AddQueryParam("redirect_uri", "http://localhost:8989/api/v3/importlist/action/ligma")
.Build();
request.Method = HttpMethod.Post;
return new { };
}
return new { };
}
public override IParseImportListResponse GetParser()
{
return new MalParser();
@ -32,7 +96,64 @@ namespace NzbDrone.Core.ImportLists.MyAnimeList
public override IImportListRequestGenerator GetRequestGenerator()
{
return new MalRequestGenerator();
return new MalRequestGenerator()
{
Settings = Settings,
};
}
private string GeneratePKCEforMal()
{
// For the sake of MAL, the code challenge is the same as the code verifie
var validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~._";
var code = new char[128];
for (var i = 0; i < code.Length; i++)
{
var selectedchar = validChars[RandomNumberGenerator.GetInt32(validChars.Length)];
code[i] = selectedchar;
}
var codeChallenge = new string(code);
_logger.Info($"MAL Code Challenge: {codeChallenge}");
return codeChallenge;
}
private class IDS
{
[JsonProperty("mal_id")]
public int MalId { get; set; }
[JsonProperty("thetvdb_id")]
public int TvdbId { get; set; }
}
private void generateDictionary(string jsonfile)
{
var ids = Json.Deserialize<List<IDS>>(jsonfile);
foreach (var id in ids)
{
Maltotvdb.Add(id.MalId, id.TvdbId);
}
}
private async void LoadMalIDConversionJson()
{
try
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync("https://github.com/Fribb/anime-lists/blob/master/anime-list-mini.json");
var result = await response.Content.ReadAsStringAsync();
generateDictionary(result);
}
catch (HttpRequestException ex)
{
_logger.Error(ex.Message);
MalIdConversions = "";
}
}
}
}

View File

@ -1,16 +1,18 @@
using System;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.ImportLists.MyAnimeList
{
internal class MalSettingsValidator : AbstractValidator<MalListSettings>
public class MalSettingsValidator : AbstractValidator<MalListSettings>
{
public MalSettingsValidator()
{
}
}
internal class MalListSettings : IImportListSettings
public class MalListSettings : IImportListSettings
{
public string BaseUrl { get; set; }
@ -19,9 +21,24 @@ namespace NzbDrone.Core.ImportLists.MyAnimeList
// This constructor is called when we try to add a new list
public MalListSettings()
{
BaseUrl = "";
BaseUrl = "https://api.myanimelist.net/v2";
}
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox)]
public string AccessToken { get; set; }
[FieldDefinition(0, Label = "Refresh Token", Type = FieldType.Textbox)]
public string RefreshToken { get; set; }
[FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox)]
public DateTime Expires { get; set; }
[FieldDefinition(0, Label = "Auth User", Type = FieldType.Textbox)]
public string AuthUser { get; set; }
[FieldDefinition(99, Label = "Auth With MAL", Type = FieldType.OAuth)]
public string SignIn { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@ -1,14 +1,28 @@
using System;
using System.Collections.Generic;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.ImportLists.MyAnimeList
{
internal class MalParser : IParseImportListResponse
public class MalParser : IParseImportListResponse
{
public IList<ImportListItemInfo> ParseResponse(ImportListResponse importListResponse)
{
throw new NotImplementedException();
var jsonResponse = Json.Deserialize<MalResponse>(importListResponse.Content);
var series = new List<ImportListItemInfo>();
foreach (var show in jsonResponse.Animes)
{
series.AddIfNotNull(new ImportListItemInfo
{
Title = show.AnimeInfo.Title,
TvdbId = MalImport.Maltotvdb.TryGetValue(show.AnimeInfo.Id, out var a) ? a : -1
});
}
return series;
}
}
}

View File

@ -1,14 +1,39 @@
using System.Collections.Generic;
using NzbDrone.Common.Http;
namespace NzbDrone.Core.ImportLists.MyAnimeList
{
internal class MalRequestGenerator : IImportListRequestGenerator
public enum MalUserListType
{
public ImportListPageableRequestChain GetListItems()
Watching,
Completed,
OnHold,
Dropped,
PlanToWatch
}
public class MalRequestGenerator : IImportListRequestGenerator
{
public MalListSettings Settings { get; set; }
public virtual ImportListPageableRequestChain GetListItems()
{
var pageableReq = new ImportListPageableRequestChain();
/*
* create
*/
pageableReq.Add(GetSeriesRequest());
return pageableReq;
}
private IEnumerable<ImportListRequest> GetSeriesRequest()
{
var url = $"{Settings.BaseUrl.Trim()}/users/@me/animelist?fields=list_status";
var httpReq = new ImportListRequest(url, HttpAccept.Json);
httpReq.HttpRequest.Headers.Add("Authorization", $"Bearer {Settings.AccessToken}");
yield return httpReq;
}
}
}