using System.Net.Http.Headers; using System.Text.Json.Serialization; using Common.Configuration; using Common.Configuration.DownloadClient; using Domain.Models.Deluge.Exceptions; using Domain.Models.Deluge.Request; using Domain.Models.Deluge.Response; using Infrastructure.Verticals.DownloadClient.Deluge.Extensions; using Microsoft.Extensions.Options; using Newtonsoft.Json; namespace Infrastructure.Verticals.DownloadClient.Deluge; public sealed class DelugeClient { private readonly DelugeConfig _config; private readonly HttpClient _httpClient; private static readonly IReadOnlyList Fields = [ "hash", "state", "name", "eta", "private", "total_done", "label", "seeding_time", "ratio", "trackers" ]; public DelugeClient(IOptions config, IHttpClientFactory httpClientFactory) { _config = config.Value; _httpClient = httpClientFactory.CreateClient(nameof(DelugeService)); } public async Task LoginAsync() { return await SendRequest("auth.login", _config.Password); } public async Task Logout() { return await SendRequest("auth.delete_session"); } public async Task> ListTorrents(Dictionary? filters = null) { filters ??= new Dictionary(); var keys = typeof(DelugeTorrent).GetAllJsonPropertyFromType(); Dictionary result = await SendRequest>("core.get_torrents_status", filters, keys); return result.Values.ToList(); } public async Task> ListTorrentsExtended(Dictionary? filters = null) { filters ??= new Dictionary(); var keys = typeof(DelugeTorrentExtended).GetAllJsonPropertyFromType(); Dictionary result = await SendRequest>("core.get_torrents_status", filters, keys); return result.Values.ToList(); } public async Task GetTorrent(string hash) { List torrents = await ListTorrents(new Dictionary() { { "hash", hash } }); return torrents.FirstOrDefault(); } public async Task GetTorrentExtended(string hash) { List torrents = await ListTorrentsExtended(new Dictionary { { "hash", hash } }); return torrents.FirstOrDefault(); } public async Task GetTorrentStatus(string hash) { return await SendRequest( "web.get_torrent_status", hash, Fields ); } public async Task?> GetStatusForAllTorrents() { Dictionary? downloads = await SendRequest?>( "core.get_torrents_status", "", Fields ); return downloads?.Values.ToList(); } public async Task GetTorrentFiles(string hash) { return await SendRequest("web.get_torrent_files", hash); } public async Task ChangeFilesPriority(string hash, List priorities) { Dictionary> filePriorities = new() { { "file_priorities", priorities } }; await SendRequest>("core.set_torrent_options", hash, filePriorities); } public async Task DeleteTorrents(List hashes) { await SendRequest>("core.remove_torrents", hashes, true); } private async Task PostJson(String json) { StringContent content = new StringContent(json); content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json"); var responseMessage = await _httpClient.PostAsync(new Uri(_config.Url, "/json"), content); responseMessage.EnsureSuccessStatusCode(); var responseJson = await responseMessage.Content.ReadAsStringAsync(); return responseJson; } private DelugeRequest CreateRequest(string method, params object[] parameters) { if (String.IsNullOrWhiteSpace(method)) { throw new ArgumentException(nameof(method)); } return new DelugeRequest(1, method, parameters); } public async Task SendRequest(string method, params object[] parameters) { return await SendRequest(CreateRequest(method, parameters)); } public async Task SendRequest(DelugeRequest webRequest) { var requestJson = JsonConvert.SerializeObject(webRequest, Formatting.None, new JsonSerializerSettings { NullValueHandling = webRequest.NullValueHandling }); var responseJson = await PostJson(requestJson); var settings = new JsonSerializerSettings { Error = (_, args) => { // Suppress the error and continue args.ErrorContext.Handled = true; } }; DelugeResponse? webResponse = JsonConvert.DeserializeObject>(responseJson, settings); if (webResponse?.Error != null) { throw new DelugeClientException(webResponse.Error.Message); } if (webResponse?.ResponseId != webRequest.RequestId) { throw new DelugeClientException("desync"); } return webResponse.Result; } }