mirror of
https://github.com/C9Glax/tranga.git
synced 2025-06-23 03:24:15 +02:00
Fix FlareSolverr, Flaresolverrsharp is broken
This commit is contained in:
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
||||||
<PackageReference Include="FlareSolverrSharp" Version="3.0.7" />
|
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.0" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.12.0" />
|
||||||
<PackageReference Include="log4net" Version="3.0.4" />
|
<PackageReference Include="log4net" Version="3.0.4" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.3" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.3" />
|
||||||
|
@ -4,7 +4,6 @@ using API.Schema;
|
|||||||
using API.Schema.Contexts;
|
using API.Schema.Contexts;
|
||||||
using API.Schema.Jobs;
|
using API.Schema.Jobs;
|
||||||
using Asp.Versioning;
|
using Asp.Versioning;
|
||||||
using FlareSolverrSharp;
|
|
||||||
using log4net;
|
using log4net;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -321,43 +320,18 @@ public class SettingsController(PgsqlContext context, ILog Log) : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Test FlareSolverr
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <response code="200">FlareSolverr is working!</response>
|
/// <response code="200">FlareSolverr is working!</response>
|
||||||
/// <response code="400">FlareSolverr URL is malformed</response>
|
|
||||||
/// <response code="500">FlareSolverr is not working</response>
|
/// <response code="500">FlareSolverr is not working</response>
|
||||||
/// <response code="503">FlareSolverr could not be reached</response>
|
|
||||||
[HttpPost("FlareSolverr/Test")]
|
[HttpPost("FlareSolverr/Test")]
|
||||||
[ProducesResponseType(Status200OK)]
|
[ProducesResponseType(Status200OK)]
|
||||||
[ProducesResponseType(Status400BadRequest)]
|
|
||||||
[ProducesResponseType(Status500InternalServerError)]
|
[ProducesResponseType(Status500InternalServerError)]
|
||||||
[ProducesResponseType(Status503ServiceUnavailable)]
|
|
||||||
public IActionResult TestFlareSolverrReachable()
|
public IActionResult TestFlareSolverrReachable()
|
||||||
{
|
{
|
||||||
const string knownProtectedUrl = "https://prowlarr.servarr.com/v1/ping";
|
const string knownProtectedUrl = "https://prowlarr.servarr.com/v1/ping";
|
||||||
HttpClient client = new();
|
FlareSolverrDownloadClient client = new();
|
||||||
if (!Uri.TryCreate(new(TrangaSettings.flareSolverrUrl), "v1", out Uri? uri))
|
RequestResult result = client.MakeRequestInternal(knownProtectedUrl);
|
||||||
return BadRequest();
|
return (int)result.statusCode >= 200 && (int)result.statusCode < 300 ? Ok() : StatusCode(500, result.statusCode);
|
||||||
HttpRequestMessage request = new(HttpMethod.Post, uri);
|
|
||||||
JObject data = new()
|
|
||||||
{
|
|
||||||
["cmd"] = "request.get",
|
|
||||||
["url"] = knownProtectedUrl
|
|
||||||
};
|
|
||||||
request.Content = new StringContent(JsonConvert.SerializeObject(data));
|
|
||||||
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
|
|
||||||
HttpResponseMessage response = client.Send(request);
|
|
||||||
if (!response.IsSuccessStatusCode)
|
|
||||||
return StatusCode(Status503ServiceUnavailable);
|
|
||||||
client = new(new ClearanceHandler(TrangaSettings.flareSolverrUrl));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
client.GetStringAsync(knownProtectedUrl).Wait();
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return StatusCode(Status500InternalServerError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ using log4net;
|
|||||||
|
|
||||||
namespace API.MangaDownloadClients;
|
namespace API.MangaDownloadClients;
|
||||||
|
|
||||||
internal abstract class DownloadClient
|
public abstract class DownloadClient
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<RequestType, DateTime> LastExecutedRateLimit = new();
|
private static readonly Dictionary<RequestType, DateTime> LastExecutedRateLimit = new();
|
||||||
protected ILog Log { get; init; }
|
protected ILog Log { get; init; }
|
||||||
|
180
API/MangaDownloadClients/FlareSolverrDownloadClient.cs
Normal file
180
API/MangaDownloadClients/FlareSolverrDownloadClient.cs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using HtmlAgilityPack;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace API.MangaDownloadClients;
|
||||||
|
|
||||||
|
public class FlareSolverrDownloadClient : DownloadClient
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
internal override RequestResult MakeRequestInternal(string url, string? referrer = null, string? clickButton = null)
|
||||||
|
{
|
||||||
|
if (clickButton is not null)
|
||||||
|
Log.Warn("Client can not click button");
|
||||||
|
if(referrer is not null)
|
||||||
|
Log.Warn("Client can not set referrer");
|
||||||
|
if (TrangaSettings.flareSolverrUrl == string.Empty)
|
||||||
|
{
|
||||||
|
Log.Error("FlareSolverr URL is empty");
|
||||||
|
return new(HttpStatusCode.InternalServerError, null, Stream.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri flareSolverrUri = new (TrangaSettings.flareSolverrUrl);
|
||||||
|
if (flareSolverrUri.Segments.Last() != "v1")
|
||||||
|
flareSolverrUri = new UriBuilder(flareSolverrUri)
|
||||||
|
{
|
||||||
|
Path = "v1"
|
||||||
|
}.Uri;
|
||||||
|
|
||||||
|
HttpClient client = new()
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(10),
|
||||||
|
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher,
|
||||||
|
DefaultRequestHeaders = { { "User-Agent", TrangaSettings.userAgent } }
|
||||||
|
};
|
||||||
|
|
||||||
|
JObject requestObj = new()
|
||||||
|
{
|
||||||
|
["cmd"] = "request.get",
|
||||||
|
["url"] = url
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpRequestMessage requestMessage = new(HttpMethod.Post, flareSolverrUri)
|
||||||
|
{
|
||||||
|
Content = new StringContent(JsonConvert.SerializeObject(requestObj)),
|
||||||
|
};
|
||||||
|
requestMessage.Content.Headers.ContentType = new ("application/json");
|
||||||
|
Log.Debug($"Requesting {url}");
|
||||||
|
|
||||||
|
HttpResponseMessage? response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = client.Send(requestMessage);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
return new (HttpStatusCode.Unused, null, Stream.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Log.Debug($"Request returned status code {(int)response.StatusCode} {response.StatusCode}:\n" +
|
||||||
|
$"=====\n" +
|
||||||
|
$"Request:\n" +
|
||||||
|
$"{requestMessage.Method} {requestMessage.RequestUri}\n" +
|
||||||
|
$"{requestMessage.Version} {requestMessage.VersionPolicy}\n" +
|
||||||
|
$"Headers:\n\t{string.Join("\n\t", requestMessage.Headers.Select(h => $"{h.Key}: <{string.Join(">, <", h.Value)}"))}>\n" +
|
||||||
|
$"{requestMessage.Content?.ReadAsStringAsync().Result}" +
|
||||||
|
$"=====\n" +
|
||||||
|
$"Response:\n" +
|
||||||
|
$"{response.Version}\n" +
|
||||||
|
$"Headers:\n\t{string.Join("\n\t", response.Headers.Select(h => $"{h.Key}: <{string.Join(">, <", h.Value)}"))}>\n" +
|
||||||
|
$"{response.Content.ReadAsStringAsync().Result}");
|
||||||
|
return new (response.StatusCode, null, Stream.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
string responseString = response.Content.ReadAsStringAsync().Result;
|
||||||
|
JObject responseObj = JObject.Parse(responseString);
|
||||||
|
if (!IsInCorrectFormat(responseObj, out string? reason))
|
||||||
|
{
|
||||||
|
Log.Error($"Wrong format: {reason}");
|
||||||
|
return new(HttpStatusCode.Unused, null, Stream.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
string statusResponse = responseObj["status"]!.Value<string>()!;
|
||||||
|
if (statusResponse != "ok")
|
||||||
|
{
|
||||||
|
Log.Debug($"Status is not ok: {statusResponse}");
|
||||||
|
return new(HttpStatusCode.Unused, null, Stream.Null);
|
||||||
|
}
|
||||||
|
JObject solution = (responseObj["solution"] as JObject)!;
|
||||||
|
|
||||||
|
if (!Enum.TryParse(solution["status"]!.Value<int>().ToString(), out HttpStatusCode statusCode))
|
||||||
|
{
|
||||||
|
Log.Error($"Wrong format: Cant parse status code: {solution["status"]!.Value<int>()}");
|
||||||
|
return new(HttpStatusCode.Unused, null, Stream.Null);
|
||||||
|
}
|
||||||
|
if (statusCode < HttpStatusCode.OK || statusCode >= HttpStatusCode.MultipleChoices)
|
||||||
|
{
|
||||||
|
Log.Debug($"Status is: {statusCode}");
|
||||||
|
return new(statusCode, null, Stream.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (solution["response"]!.Value<string>() is not { } htmlString)
|
||||||
|
{
|
||||||
|
Log.Error("Wrong format: Cant find response in solution");
|
||||||
|
return new(HttpStatusCode.Unused, null, Stream.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsJson(htmlString, out HtmlDocument document, out string? json))
|
||||||
|
{
|
||||||
|
MemoryStream ms = new();
|
||||||
|
ms.Write(Encoding.UTF8.GetBytes(json));
|
||||||
|
ms.Position = 0;
|
||||||
|
return new(statusCode, document, ms);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemoryStream ms = new();
|
||||||
|
ms.Write(Encoding.UTF8.GetBytes(htmlString));
|
||||||
|
ms.Position = 0;
|
||||||
|
return new(statusCode, document, ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsInCorrectFormat(JObject responseObj, [NotNullWhen(false)]out string? reason)
|
||||||
|
{
|
||||||
|
reason = null;
|
||||||
|
if (!responseObj.ContainsKey("status"))
|
||||||
|
{
|
||||||
|
reason = "Cant find status on response";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseObj["solution"] is not JObject solution)
|
||||||
|
{
|
||||||
|
reason = "Cant find solution";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!solution.ContainsKey("status"))
|
||||||
|
{
|
||||||
|
reason = "Wrong format: Cant find status in solution";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!solution.ContainsKey("response"))
|
||||||
|
{
|
||||||
|
|
||||||
|
reason = "Wrong format: Cant find response in solution";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsJson(string htmlString, out HtmlDocument document, [NotNullWhen(true)]out string? jsonString)
|
||||||
|
{
|
||||||
|
jsonString = null;
|
||||||
|
document = new();
|
||||||
|
document.LoadHtml(htmlString);
|
||||||
|
|
||||||
|
HtmlNode pre = document.DocumentNode.SelectSingleNode("//pre");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JObject.Parse(pre.InnerText);
|
||||||
|
jsonString = pre.InnerText;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (JsonReaderException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using FlareSolverrSharp;
|
|
||||||
using HtmlAgilityPack;
|
using HtmlAgilityPack;
|
||||||
|
|
||||||
namespace API.MangaDownloadClients;
|
namespace API.MangaDownloadClients;
|
||||||
@ -10,9 +9,7 @@ internal class HttpDownloadClient : DownloadClient
|
|||||||
{
|
{
|
||||||
if (clickButton is not null)
|
if (clickButton is not null)
|
||||||
Log.Warn("Client can not click button");
|
Log.Warn("Client can not click button");
|
||||||
HttpClient client = TrangaSettings.flareSolverrUrl == string.Empty
|
HttpClient client = new();
|
||||||
? new ()
|
|
||||||
: new (new ClearanceHandler(TrangaSettings.flareSolverrUrl));
|
|
||||||
client.Timeout = TimeSpan.FromSeconds(10);
|
client.Timeout = TimeSpan.FromSeconds(10);
|
||||||
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
|
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
|
||||||
client.DefaultRequestHeaders.Add("User-Agent", TrangaSettings.userAgent);
|
client.DefaultRequestHeaders.Add("User-Agent", TrangaSettings.userAgent);
|
||||||
@ -46,7 +43,7 @@ internal class HttpDownloadClient : DownloadClient
|
|||||||
$"{response.Version}\n" +
|
$"{response.Version}\n" +
|
||||||
$"Headers:\n\t{string.Join("\n\t", response.Headers.Select(h => $"{h.Key}: <{string.Join(">, <", h.Value)}"))}>\n" +
|
$"Headers:\n\t{string.Join("\n\t", response.Headers.Select(h => $"{h.Key}: <{string.Join(">, <", h.Value)}"))}>\n" +
|
||||||
$"{response.Content.ReadAsStringAsync().Result}");
|
$"{response.Content.ReadAsStringAsync().Result}");
|
||||||
return new (response.StatusCode, null, Stream.Null);
|
return new FlareSolverrDownloadClient().MakeRequestInternal(url, referrer, clickButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream stream;
|
Stream stream;
|
||||||
|
@ -92,7 +92,6 @@ Endpoints are documented in Swagger. Just spin up an instance, and go to `http:/
|
|||||||
- [Ngpsql](https://github.com/npgsql/npgsql/blob/main/LICENSE)
|
- [Ngpsql](https://github.com/npgsql/npgsql/blob/main/LICENSE)
|
||||||
- [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md)
|
- [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md)
|
||||||
- [PuppeteerSharp](https://github.com/hardkoded/puppeteer-sharp/blob/master/LICENSE)
|
- [PuppeteerSharp](https://github.com/hardkoded/puppeteer-sharp/blob/master/LICENSE)
|
||||||
- [FlareSolverrSharp](https://github.com/FlareSolverr/FlareSolverrSharp)
|
|
||||||
- [Html Agility Pack (HAP)](https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE)
|
- [Html Agility Pack (HAP)](https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE)
|
||||||
- [Soenneker.Utils.String.NeedlemanWunsch](https://github.com/soenneker/soenneker.utils.string.needlemanwunsch/blob/main/LICENSE)
|
- [Soenneker.Utils.String.NeedlemanWunsch](https://github.com/soenneker/soenneker.utils.string.needlemanwunsch/blob/main/LICENSE)
|
||||||
- [Sixlabors.ImageSharp](https://docs-v2.sixlabors.com/articles/imagesharp/index.html#license)
|
- [Sixlabors.ImageSharp](https://docs-v2.sixlabors.com/articles/imagesharp/index.html#license)
|
||||||
|
Reference in New Issue
Block a user