Add project files.

This commit is contained in:
ahjephson
2024-04-22 14:15:07 +01:00
parent ce7b627fa9
commit f9847c60f5
166 changed files with 14345 additions and 0 deletions

View File

@@ -0,0 +1,951 @@
using Lantean.QBitTorrentClient.Models;
using System;
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
namespace Lantean.QBitTorrentClient
{
public class ApiClient : IApiClient
{
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _options = SerializerOptions.Options;
public ApiClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
#region Authentication
public async Task<bool> CheckAuthState()
{
try
{
var response = await _httpClient.GetAsync("app/version");
return response.StatusCode == HttpStatusCode.OK;
}
catch
{
return false;
}
}
public async Task Login(string username, string password)
{
var content = new FormUrlEncodedBuilder()
.Add("username", username)
.Add("password", password)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("auth/login", content);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
if (responseContent == "Fails.")
{
throw new HttpRequestException(null, null, HttpStatusCode.BadRequest);
}
}
public async Task Logout()
{
var response = await _httpClient.PostAsync("auth/logout", null);
response.EnsureSuccessStatusCode();
}
#endregion Authentication
#region Application
public async Task<string> GetApplicationVersion()
{
var response = await _httpClient.GetAsync("app/version");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
public async Task<string> GetAPIVersion()
{
var response = await _httpClient.GetAsync("app/webapiVersion");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
public async Task<BuildInfo> GetBuildInfo()
{
var response = await _httpClient.GetAsync("app/buildInfo");
response.EnsureSuccessStatusCode();
return await GetJson<BuildInfo>(response.Content);
}
public async Task Shutdown()
{
var response = await _httpClient.PostAsync("app/shutdown", null);
response.EnsureSuccessStatusCode();
}
public async Task<Preferences> GetApplicationPreferences()
{
var response = await _httpClient.GetAsync("app/preferences");
response.EnsureSuccessStatusCode();
return await GetJson<Preferences>(response.Content);
}
public async Task SetApplicationPreferences(UpdatePreferences preferences)
{
var json = JsonSerializer.Serialize(preferences, _options);
var content = new FormUrlEncodedBuilder()
.Add("json", json)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("app/setPreferences", content);
response.EnsureSuccessStatusCode();
}
public async Task<string> GetDefaultSavePath()
{
var response = await _httpClient.GetAsync("app/defaultSavePath");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
#endregion Application
#region Log
public async Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null)
{
var query = new QueryBuilder();
if (normal is not null)
{
query.Add("normal", normal.Value);
}
if (info is not null)
{
query.Add("info", info.Value);
}
if (warning is not null)
{
query.Add("warning", warning.Value);
}
if (critical is not null)
{
query.Add("critical", critical.Value);
}
if (lastKnownId is not null)
{
query.Add("last_known_id", lastKnownId.Value);
}
var response = await _httpClient.GetAsync($"log/main", query);
response.EnsureSuccessStatusCode();
return await GetJsonList<Log>(response.Content);
}
public async Task<IReadOnlyList<PeerLog>> GetPeerLog(int? lastKnownId = null)
{
var query = new QueryBuilder();
if (lastKnownId is not null)
{
query.Add("last_known_id", lastKnownId.Value);
}
var response = await _httpClient.GetAsync($"log/peers", query);
response.EnsureSuccessStatusCode();
return await GetJsonList<PeerLog>(response.Content);
}
#endregion Log
#region Sync
public async Task<MainData> GetMainData(int requestId)
{
var response = await _httpClient.GetAsync($"sync/maindata?rid={requestId}");
response.EnsureSuccessStatusCode();
return await GetJson<MainData>(response.Content);
}
public async Task<TorrentPeers> GetTorrentPeersData(string hash, int requestId)
{
var response = await _httpClient.GetAsync($"sync/torrentPeers?hash={hash}&rid={requestId}");
response.EnsureSuccessStatusCode();
return await GetJson<TorrentPeers>(response.Content);
}
#endregion Sync
#region Transfer info
public async Task<GlobalTransferInfo> GetGlobalTransferInfo()
{
var response = await _httpClient.GetAsync("transfer/info");
response.EnsureSuccessStatusCode();
return await GetJson<GlobalTransferInfo>(response.Content);
}
public async Task<bool> GetAlternativeSpeedLimitsState()
{
var response = await _httpClient.GetAsync("transfer/speedLimitsMode");
response.EnsureSuccessStatusCode();
var value = await response.Content.ReadAsStringAsync();
return value == "1";
}
public async Task ToggleAlternativeSpeedLimits()
{
var response = await _httpClient.PostAsync("transfer/toggleSpeedLimitsMode", null);
response.EnsureSuccessStatusCode();
}
public async Task<long> GetGlobalDownloadLimit()
{
var response = await _httpClient.GetAsync("transfer/downloadLimit");
response.EnsureSuccessStatusCode();
var value = await response.Content.ReadAsStringAsync();
return long.Parse(value);
}
public async Task SetGlobalDownloadLimit(long limit)
{
var content = new FormUrlEncodedBuilder()
.Add("limit", limit)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("transfer/setDownloadLimit", content);
response.EnsureSuccessStatusCode();
}
public async Task<long> GetGlobalUploadLimit()
{
var response = await _httpClient.GetAsync("transfer/uploadLimit");
response.EnsureSuccessStatusCode();
var value = await response.Content.ReadAsStringAsync();
return long.Parse(value);
}
public async Task SetGlobalUploadLimit(long limit)
{
var content = new FormUrlEncodedBuilder()
.Add("limit", limit)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("transfer/setUploadLimit", content);
response.EnsureSuccessStatusCode();
}
public async Task BanPeers(IEnumerable<PeerId> peers)
{
var content = new FormUrlEncodedBuilder()
.AddPipeSeparated("peers", peers)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("transfer/banPeers", content);
response.EnsureSuccessStatusCode();
}
#endregion Transfer info
#region Torrent management
public async Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, params string[] hashes)
{
var query = new QueryBuilder();
if (filter is not null)
{
query.Add("filter", filter);
}
if (category is not null)
{
query.Add("category", category);
}
if (tag is not null)
{
query.Add("tag", tag);
}
if (sort is not null)
{
query.Add("sort", sort);
}
if (reverse is not null)
{
query.Add("reverse", reverse.Value);
}
if (limit is not null)
{
query.Add("limit", limit.Value);
}
if (offset is not null)
{
query.Add("offset", offset.Value);
}
if (hashes.Length > 0)
{
query.Add("hashes", string.Join('|', hashes));
}
var response = await _httpClient.GetAsync("torrents/info", query);
response.EnsureSuccessStatusCode();
return await GetJsonList<Torrent>(response.Content);
}
public async Task<TorrentProperties> GetTorrentProperties(string hash)
{
var response = await _httpClient.GetAsync($"torrents/properties?hash={hash}");
response.EnsureSuccessStatusCode();
return await GetJson<TorrentProperties>(response.Content);
}
public async Task<IReadOnlyList<TorrentTrackers>> GetTorrentTrackers(string hash)
{
var response = await _httpClient.GetAsync($"torrents/trackers?hash={hash}");
response.EnsureSuccessStatusCode();
return await GetJsonList<TorrentTrackers>(response.Content);
}
public async Task<IReadOnlyList<WebSeed>> GetTorrentWebSeeds(string hash)
{
var response = await _httpClient.GetAsync($"torrents/webseeds?hash={hash}");
response.EnsureSuccessStatusCode();
return await GetJsonList<WebSeed>(response.Content);
}
public async Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes)
{
var query = new QueryBuilder();
query.Add("hash", hash);
if (indexes.Length > 0)
{
query.Add("indexes", string.Join('|', indexes));
}
var response = await _httpClient.GetAsync("torrents/files", query);
response.EnsureSuccessStatusCode();
return await GetJsonList<FileData>(response.Content);
}
public async Task<IReadOnlyList<PieceState>> GetTorrentPieceStates(string hash)
{
var response = await _httpClient.GetAsync($"torrents/pieceStates?hash={hash}");
response.EnsureSuccessStatusCode();
return await GetJsonList<PieceState>(response.Content);
}
public async Task<IReadOnlyList<string>> GetTorrentPieceHashes(string hash)
{
var response = await _httpClient.GetAsync($"torrents/pieceHashes?hash={hash}");
response.EnsureSuccessStatusCode();
return await GetJsonList<string>(response.Content);
}
public async Task PauseTorrents(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/pause", content);
response.EnsureSuccessStatusCode();
}
public async Task ResumeTorrents(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/resume", content);
response.EnsureSuccessStatusCode();
}
public async Task DeleteTorrents(bool? all = null, bool deleteFiles = false, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("deleteFiles", deleteFiles)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/delete", content);
response.EnsureSuccessStatusCode();
}
public async Task RecheckTorrents(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/recheck", content);
response.EnsureSuccessStatusCode();
}
public async Task ReannounceTorrents(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/reannounce", content);
response.EnsureSuccessStatusCode();
}
public async Task AddTorrent(IEnumerable<string>? urls = null, Dictionary<string, Stream>? torrents = null, string? savePath = null, string? cookie = null, string? category = null, IEnumerable<string>? tags = null, bool? skipChecking = null, bool? paused = null, string? contentLayout = null, string? renameTorrent = null, long? uploadLimit = null, long? downloadLimit = null, float? ratioLimit = null, int? seedingTimeLimit = null, bool? autoTorrentManagement = null, bool? sequentialDownload = null, bool? firstLastPiecePriority = null)
{
var content = new MultipartFormDataContent();
if (urls is not null)
{
content.AddString("urls", string.Join('\n', urls));
}
if (torrents is not null)
{
foreach (var (name, stream) in torrents)
{
content.Add(new StreamContent(stream), name);
}
}
if (savePath is not null)
{
content.AddString("savepath", savePath);
}
if (cookie is not null)
{
content.AddString("cookie", cookie);
}
if (category is not null)
{
content.AddString("category", category);
}
if (tags is not null)
{
content.AddString("tags", string.Join(',', tags));
}
if (skipChecking is not null)
{
content.AddString("skip_checking", skipChecking.Value);
}
if (paused is not null)
{
content.AddString("paused", paused.Value);
}
if (contentLayout is not null)
{
content.AddString("contentLayout", contentLayout);
}
if (renameTorrent is not null)
{
content.AddString("rename", renameTorrent);
}
if (uploadLimit is not null)
{
content.AddString("upLimit", uploadLimit.Value);
}
if (downloadLimit is not null)
{
content.AddString("dlLimit", downloadLimit.Value);
}
if (ratioLimit is not null)
{
content.AddString("ratioLimit", ratioLimit.Value);
}
if (seedingTimeLimit is not null)
{
content.AddString("seedingTimeLimit", seedingTimeLimit.Value);
}
if (autoTorrentManagement is not null)
{
content.AddString("autoTMM", autoTorrentManagement.Value);
}
if (sequentialDownload is not null)
{
content.AddString("sequentialDownload", sequentialDownload.Value);
}
if (firstLastPiecePriority is not null)
{
content.AddString("firstLastPiecePrio", firstLastPiecePriority.Value);
}
var response = await _httpClient.PostAsync("torrents/add", content);
response.EnsureSuccessStatusCode();
}
public async Task AddTrackersToTorrent(string hash, IEnumerable<string> urls)
{
var content = new FormUrlEncodedBuilder()
.Add("hash", hash)
.Add("urls", string.Join('\n', urls))
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/addTrackers", content);
response.EnsureSuccessStatusCode();
}
public async Task EditTracker(string hash, string originalUrl, string newUrl)
{
var content = new FormUrlEncodedBuilder()
.Add("hash", hash)
.Add("originalUrl", originalUrl)
.Add("newUrl", newUrl)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/editTracker", content);
response.EnsureSuccessStatusCode();
}
public async Task RemoveTrackers(string hash, IEnumerable<string> urls)
{
var content = new FormUrlEncodedBuilder()
.Add("hash", hash)
.AddPipeSeparated("urls", urls)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/removeTrackers", content);
response.EnsureSuccessStatusCode();
}
public async Task AddPeers(IEnumerable<string> hashes, IEnumerable<PeerId> peers)
{
var content = new FormUrlEncodedBuilder()
.AddPipeSeparated("hash", hashes)
.AddPipeSeparated("urls", peers)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/addPeers", content);
response.EnsureSuccessStatusCode();
}
public async Task IncreaseTorrentPriority(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hash", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/increasePrio", content);
response.EnsureSuccessStatusCode();
}
public async Task DecreaseTorrentPriority(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hash", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/decreasePrio", content);
response.EnsureSuccessStatusCode();
}
public async Task MaximalTorrentPriority(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hash", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/topPrio", content);
response.EnsureSuccessStatusCode();
}
public async Task MinimalTorrentPriority(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hash", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/bottomPrio", content);
response.EnsureSuccessStatusCode();
}
public async Task SetFilePriority(string hash, IEnumerable<int> id, Priority priority)
{
var content = new FormUrlEncodedBuilder()
.Add("hash", hash)
.AddPipeSeparated("id", id)
.Add("priority", priority)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/filePrio", content);
response.EnsureSuccessStatusCode();
}
public async Task<IReadOnlyDictionary<string, long>> GetTorrentDownloadLimit(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/downloadLimit", content);
response.EnsureSuccessStatusCode();
return await GetJsonDictionary<string, long>(response.Content);
}
public async Task SetTorrentDownloadLimit(long limit, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("limit", limit)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setDownloadLimit", content);
response.EnsureSuccessStatusCode();
}
public async Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("ratioLimit", ratioLimit)
.Add("seedingTimeLimit", seedingTimeLimit)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setShareLimits", content);
response.EnsureSuccessStatusCode();
}
public async Task<IReadOnlyDictionary<string, long>> GetTorrentUploadLimit(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/uploadLimit", content);
response.EnsureSuccessStatusCode();
return await GetJsonDictionary<string, long>(response.Content);
}
public async Task SetTorrentUploadLimit(long limit, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("limit", limit)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setUploadLimit", content);
response.EnsureSuccessStatusCode();
}
public async Task SetTorrentLocation(string location, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("location", location)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setLocation", content);
response.EnsureSuccessStatusCode();
}
public async Task SetTorrentName(string name, string hash)
{
var content = new FormUrlEncodedBuilder()
.Add("hash", hash)
.Add("name", name)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/rename", content);
response.EnsureSuccessStatusCode();
}
public async Task SetTorrentCategory(string category, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("category", category)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setCategory", content);
response.EnsureSuccessStatusCode();
}
public async Task<IReadOnlyDictionary<string, Category>> GetAllCategories()
{
var response = await _httpClient.GetAsync("torrents/categories");
response.EnsureSuccessStatusCode();
return await GetJsonDictionary<string, Category>(response.Content);
}
public async Task AddCategory(string category, string savePath)
{
var content = new FormUrlEncodedBuilder()
.Add("category", category)
.Add("savePath", savePath)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/createCategory", content);
response.EnsureSuccessStatusCode();
}
public async Task EditCategory(string category, string savePath)
{
var content = new FormUrlEncodedBuilder()
.Add("category", category)
.Add("savePath", savePath)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/editCategory", content);
response.EnsureSuccessStatusCode();
}
public async Task RemoveCategories(params string[] categories)
{
var content = new FormUrlEncodedBuilder()
.Add("categories", string.Join('\n', categories))
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/removeCategories", content);
response.EnsureSuccessStatusCode();
}
public async Task AddTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.AddPipeSeparated("tags", tags)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/addTags", content);
response.EnsureSuccessStatusCode();
}
public async Task RemoveTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.AddPipeSeparated("tags", tags)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/removeTags", content);
response.EnsureSuccessStatusCode();
}
public async Task<IReadOnlyList<string>> GetAllTags()
{
var response = await _httpClient.GetAsync("torrents/tags");
response.EnsureSuccessStatusCode();
return await GetJsonList<string>(response.Content);
}
public async Task CreateTags(IEnumerable<string> tags)
{
var content = new FormUrlEncodedBuilder()
.AddPipeSeparated("tags", tags)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/createTags", content);
response.EnsureSuccessStatusCode();
}
public async Task DeleteTags(IEnumerable<string> tags)
{
var content = new FormUrlEncodedBuilder()
.AddPipeSeparated("tags", tags)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/deleteTags", content);
response.EnsureSuccessStatusCode();
}
public async Task SetAutomaticTorrentManagement(bool enable, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("enable", enable)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setAutoManagement", content);
response.EnsureSuccessStatusCode();
}
public async Task ToggleSequentialDownload(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/toggleSequentialDownload", content);
response.EnsureSuccessStatusCode();
}
public async Task SetFirstLastPiecePriority(bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/toggleFirstLastPiecePrio", content);
response.EnsureSuccessStatusCode();
}
public async Task SetForceStart(bool value, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("enable", value)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setFOrceStart", content);
response.EnsureSuccessStatusCode();
}
public async Task SetSuperSeeding(bool value, bool? all = null, params string[] hashes)
{
var content = new FormUrlEncodedBuilder()
.AddAllOrPipeSeparated("hashes", all, hashes)
.Add("enable", value)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/setSuperSeeding", content);
response.EnsureSuccessStatusCode();
}
public async Task RenameFile(string hash, string oldPath, string newPath)
{
var content = new FormUrlEncodedBuilder()
.Add("hash", hash)
.Add("oldPath", oldPath)
.Add("newPath", newPath)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/renameFile", content);
response.EnsureSuccessStatusCode();
}
public async Task RenameFolder(string hash, string oldPath, string newPath)
{
var content = new FormUrlEncodedBuilder()
.Add("hash", hash)
.Add("oldPath", oldPath)
.Add("newPath", newPath)
.ToFormUrlEncodedContent();
var response = await _httpClient.PostAsync("torrents/renameFolder", content);
response.EnsureSuccessStatusCode();
}
#endregion Torrent management
#region RSS
// not implementing RSS right now
#endregion RSS
#region Search
// not implementing Search right now
#endregion Search
private async Task<T> GetJson<T>(HttpContent content)
{
return await content.ReadFromJsonAsync<T>(_options) ?? throw new InvalidOperationException($"Unable to deserialize response as {typeof(T).Name}");
}
private async Task<IReadOnlyList<T>> GetJsonList<T>(HttpContent content)
{
var items = await GetJson<IEnumerable<T>>(content);
return items.ToList().AsReadOnly();
}
private async Task<IReadOnlyDictionary<TKey, TValue>> GetJsonDictionary<TKey, TValue>(HttpContent content) where TKey : notnull
{
var items = await GetJson<IDictionary<TKey, TValue>>(content);
return items.AsReadOnly();
}
}
}

View File

@@ -0,0 +1,109 @@
using Lantean.QBitTorrentClient.Models;
namespace Lantean.QBitTorrentClient
{
public static class ApiClientExtensions
{
public static Task PauseTorrent(this IApiClient apiClient, string hash)
{
return apiClient.PauseTorrents(null, hash);
}
public static Task PauseTorrents(this IApiClient apiClient, IEnumerable<string> hashes)
{
return apiClient.PauseTorrents(null, hashes.ToArray());
}
public static Task PauseAllTorrents(this IApiClient apiClient)
{
return apiClient.PauseTorrents(true);
}
public static Task ResumeTorrent(this IApiClient apiClient, string hash)
{
return apiClient.ResumeTorrents(null, hash);
}
public static Task ResumeTorrents(this IApiClient apiClient, IEnumerable<string> hashes)
{
return apiClient.ResumeTorrents(null, hashes.ToArray());
}
public static Task ResumeAllTorrents(this IApiClient apiClient)
{
return apiClient.ResumeTorrents(true);
}
public static Task DeleteTorrent(this IApiClient apiClient, string hash, bool deleteFiles)
{
return apiClient.DeleteTorrents(null, deleteFiles, hash);
}
public static Task DeleteTorrents(this IApiClient apiClient, IEnumerable<string> hashes, bool deleteFiles)
{
return apiClient.DeleteTorrents(null, deleteFiles, hashes.ToArray());
}
public static Task DeleteAllTorrents(this IApiClient apiClient, bool deleteFiles)
{
return apiClient.DeleteTorrents(true, deleteFiles);
}
public static async Task<Torrent?> GetTorrent(this IApiClient apiClient, string hash)
{
var torrents = await apiClient.GetTorrentList(hashes: hash);
if (torrents.Count == 0)
{
return null;
}
return torrents[0];
}
public static Task SetTorrentCategory(this IApiClient apiClient, string category, string hash)
{
return apiClient.SetTorrentCategory(category, null, hash);
}
public static Task RemoveTorrentTags(this IApiClient apiClient, IEnumerable<string> tags, string hash)
{
return apiClient.RemoveTorrentTags(tags, null, hash);
}
public static Task RemoveTorrentTag(this IApiClient apiClient, string tag, string hash)
{
return apiClient.RemoveTorrentTags([tag], hash);
}
public static Task RemoveTorrentTag(this IApiClient apiClient, string tag, IEnumerable<string> hashes)
{
return apiClient.RemoveTorrentTags([tag], null, hashes.ToArray());
}
public static Task AddTorrentTags(this IApiClient apiClient, IEnumerable<string> tags, string hash)
{
return apiClient.AddTorrentTags(tags, null, hash);
}
public static Task AddTorrentTag(this IApiClient apiClient, string tag, string hash)
{
return apiClient.AddTorrentTags([tag], hash);
}
public static Task AddTorrentTag(this IApiClient apiClient, string tag, IEnumerable<string> hashes)
{
return apiClient.AddTorrentTags([tag], null, hashes.ToArray());
}
public static Task RecheckTorrent(this IApiClient apiClient, string hash)
{
return apiClient.RecheckTorrents(null, hash);
}
public static Task ReannounceTorrent(this IApiClient apiClient, string hash)
{
return apiClient.ReannounceTorrents(null, hash);
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Converters
{
internal class CommaSeparatedJsonConverter : JsonConverter<IReadOnlyList<string>>
{
public override IReadOnlyList<string>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.String)
{
throw new JsonException("Must be of type string.");
}
List<string> list;
var value = reader.GetString();
if (value is null)
{
list = [];
}
else
{
var values = value.Split(',');
list = [.. values];
}
return list.AsReadOnly();
}
public override void Write(Utf8JsonWriter writer, IReadOnlyList<string> value, JsonSerializerOptions options)
{
var output = string.Join(',', value);
writer.WriteStringValue(output);
}
}
}

View File

@@ -0,0 +1,40 @@
using Lantean.QBitTorrentClient.Models;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Converters
{
public class SaveLocationJsonConverter : JsonConverter<SaveLocation>
{
public override SaveLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
return SaveLocation.Create(reader.GetString());
}
if (reader.TokenType == JsonTokenType.Number)
{
return SaveLocation.Create(reader.GetInt32());
}
throw new JsonException($"Unsupported token type {reader.TokenType}");
}
public override void Write(Utf8JsonWriter writer, SaveLocation value, JsonSerializerOptions options)
{
if (value.IsWatchedFolder)
{
writer.WriteNumberValue(0);
}
else if (value.IsDefaltFolder)
{
writer.WriteNumberValue(1);
}
else if (value.SavePath is not null)
{
writer.WriteStringValue(value.SavePath);
}
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Converters
{
internal class StringFloatJsonConverter : JsonConverter<float>
{
public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
if (float.TryParse(reader.GetString(), out var value))
{
return value;
}
return 0;
}
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetSingle(out var value))
{
return value;
}
return 0;
}
return 0;
}
public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
}

View File

@@ -0,0 +1,38 @@
namespace Lantean.QBitTorrentClient
{
public class FormUrlEncodedBuilder
{
private readonly IList<KeyValuePair<string, string>> _parameters;
public FormUrlEncodedBuilder()
{
_parameters = [];
}
public FormUrlEncodedBuilder(IList<KeyValuePair<string, string>> parameters)
{
_parameters = parameters;
}
public FormUrlEncodedBuilder Add(string key, string value)
{
_parameters.Add(new KeyValuePair<string, string>(key, value));
return this;
}
public FormUrlEncodedBuilder AddIfNotNullOrEmpty(string key, string value)
{
if (!string.IsNullOrEmpty(value))
{
_parameters.Add(new KeyValuePair<string, string>(key, value));
}
return this;
}
public FormUrlEncodedContent ToFormUrlEncodedContent()
{
return new FormUrlEncodedContent(_parameters);
}
}
}

View File

@@ -0,0 +1,52 @@
using System.Globalization;
namespace Lantean.QBitTorrentClient
{
public static class FormUrlEncodedBuilderExtensions
{
public static FormUrlEncodedBuilder Add(this FormUrlEncodedBuilder builder, string key, bool value)
{
return builder.Add(key, value ? "true" : "false");
}
public static FormUrlEncodedBuilder Add(this FormUrlEncodedBuilder builder, string key, int value)
{
return builder.Add(key, value.ToString());
}
public static FormUrlEncodedBuilder Add(this FormUrlEncodedBuilder builder, string key, long value)
{
return builder.Add(key, value.ToString());
}
public static FormUrlEncodedBuilder Add(this FormUrlEncodedBuilder builder, string key, DateTimeOffset value, bool useSeconds = true)
{
return builder.Add(key, useSeconds ? value.ToUnixTimeSeconds() : value.ToUnixTimeMilliseconds());
}
public static FormUrlEncodedBuilder Add(this FormUrlEncodedBuilder builder, string key, float value)
{
return builder.Add(key, value.ToString());
}
public static FormUrlEncodedBuilder Add<T>(this FormUrlEncodedBuilder builder, string key, T value) where T : struct, IConvertible
{
return builder.Add(key, value.ToInt32(CultureInfo.InvariantCulture).ToString());
}
public static FormUrlEncodedBuilder AddAllOrPipeSeparated(this FormUrlEncodedBuilder builder, string key, bool? all = null, params string[] values)
{
return builder.Add(key, all.GetValueOrDefault() ? "all" : string.Join('|', values));
}
public static FormUrlEncodedBuilder AddPipeSeparated<T>(this FormUrlEncodedBuilder builder, string key, IEnumerable<T> values)
{
return builder.Add(key, string.Join('|', values));
}
public static FormUrlEncodedBuilder AddCommaSeparated<T>(this FormUrlEncodedBuilder builder, string key, IEnumerable<T> values)
{
return builder.Add(key, string.Join(',', values));
}
}
}

View File

@@ -0,0 +1,15 @@
namespace Lantean.QBitTorrentClient
{
internal static class HttpClientExtensions
{
public static Task<HttpResponseMessage> PostAsync(this HttpClient httpClient, string requestUrl, FormUrlEncodedBuilder builder)
{
return httpClient.PostAsync(requestUrl, builder.ToFormUrlEncodedContent());
}
public static Task<HttpResponseMessage> GetAsync(this HttpClient httpClient, string requestUrl, QueryBuilder builder)
{
return httpClient.GetAsync($"{requestUrl}{builder.ToQueryString()}");
}
}
}

View File

@@ -0,0 +1,179 @@
using Lantean.QBitTorrentClient.Models;
namespace Lantean.QBitTorrentClient
{
public interface IApiClient
{
#region Authentication
Task<bool> CheckAuthState();
Task Login(string username, string password);
Task Logout();
#endregion Authentication
#region Application
Task<string> GetApplicationVersion();
Task<string> GetAPIVersion();
Task<BuildInfo> GetBuildInfo();
Task Shutdown();
Task<Preferences> GetApplicationPreferences();
Task SetApplicationPreferences(UpdatePreferences preferences);
Task<string> GetDefaultSavePath();
#endregion Application
#region Log
Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null);
Task<IReadOnlyList<PeerLog>> GetPeerLog(int? lastKnownId = null);
#endregion Log
#region Sync
Task<MainData> GetMainData(int requestId);
Task<TorrentPeers> GetTorrentPeersData(string hash, int requestId);
#endregion Sync
#region Transfer info
Task<GlobalTransferInfo> GetGlobalTransferInfo();
Task<bool> GetAlternativeSpeedLimitsState();
Task ToggleAlternativeSpeedLimits();
Task<long> GetGlobalDownloadLimit();
Task SetGlobalDownloadLimit(long limit);
Task<long> GetGlobalUploadLimit();
Task SetGlobalUploadLimit(long limit);
Task BanPeers(IEnumerable<PeerId> peers);
#endregion Transfer info
#region Torrent management
Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, params string[] hashes);
Task<TorrentProperties> GetTorrentProperties(string hash);
Task<IReadOnlyList<TorrentTrackers>> GetTorrentTrackers(string hash);
Task<IReadOnlyList<WebSeed>> GetTorrentWebSeeds(string hash);
Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes);
Task<IReadOnlyList<PieceState>> GetTorrentPieceStates(string hash);
Task<IReadOnlyList<string>> GetTorrentPieceHashes(string hash);
Task PauseTorrents(bool? all = null, params string[] hashes);
Task ResumeTorrents(bool? all = null, params string[] hashes);
Task DeleteTorrents(bool? all = null, bool deleteFiles = false, params string[] hashes);
Task RecheckTorrents(bool? all = null, params string[] hashes);
Task ReannounceTorrents(bool? all = null, params string[] hashes);
Task AddTorrent(IEnumerable<string>? urls = null, Dictionary<string, Stream>? torrents = null, string? savePath = null, string? cookie = null, string? category = null, IEnumerable<string>? tags = null, bool? skipChecking = null, bool? paused = null, string? contentLayout = null, string? renameTorrent = null, long? uploadLimit = null, long? downloadLimit = null, float? ratioLimit = null, int? seedingTimeLimit = null, bool? autoTorrentManagement = null, bool? sequentialDownload = null, bool? firstLastPiecePriority = null);
Task AddTrackersToTorrent(string hash, IEnumerable<string> urls);
Task EditTracker(string hash, string originalUrl, string newUrl);
Task RemoveTrackers(string hash, IEnumerable<string> urls);
Task AddPeers(IEnumerable<string> hashes, IEnumerable<PeerId> peers);
Task IncreaseTorrentPriority(bool? all = null, params string[] hashes);
Task DecreaseTorrentPriority(bool? all = null, params string[] hashes);
Task MaximalTorrentPriority(bool? all = null, params string[] hashes);
Task MinimalTorrentPriority(bool? all = null, params string[] hashes);
Task SetFilePriority(string hash, IEnumerable<int> id, Priority priority);
Task<IReadOnlyDictionary<string, long>> GetTorrentDownloadLimit(bool? all = null, params string[] hashes);
Task SetTorrentDownloadLimit(long limit, bool? all = null, params string[] hashes);
Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, bool? all = null, params string[] hashes);
Task<IReadOnlyDictionary<string, long>> GetTorrentUploadLimit(bool? all = null, params string[] hashes);
Task SetTorrentUploadLimit(long limit, bool? all = null, params string[] hashes);
Task SetTorrentLocation(string location, bool? all = null, params string[] hashes);
Task SetTorrentName(string name, string hash);
Task SetTorrentCategory(string category, bool? all = null, params string[] hashes);
Task<IReadOnlyDictionary<string, Category>> GetAllCategories();
Task AddCategory(string category, string savePath);
Task EditCategory(string category, string savePath);
Task RemoveCategories(params string[] categories);
Task AddTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes);
Task RemoveTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes);
Task<IReadOnlyList<string>> GetAllTags();
Task CreateTags(IEnumerable<string> tags);
Task DeleteTags(IEnumerable<string> tags);
Task SetAutomaticTorrentManagement(bool enable, bool? all = null, params string[] hashes);
Task ToggleSequentialDownload(bool? all = null, params string[] hashes);
Task SetFirstLastPiecePriority(bool? all = null, params string[] hashes);
Task SetForceStart(bool value, bool? all = null, params string[] hashes);
Task SetSuperSeeding(bool value, bool? all = null, params string[] hashes);
Task RenameFile(string hash, string oldPath, string newPath);
Task RenameFolder(string hash, string oldPath, string newPath);
#endregion Torrent management
#region RSS
// not implementing RSS right now
#endregion RSS
#region Search
// not implementing Search right now
#endregion Search
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,9 @@
namespace Lantean.QBitTorrentClient
{
public static class Limits
{
public const long GlobalLimit = -2;
public const long NoLimit = -1;
}
}

View File

@@ -0,0 +1,367 @@
using Lantean.QBitTorrentClient.Models;
namespace Lantean.QBitTorrentClient
{
public class MockApiClient : IApiClient
{
private readonly ApiClient _apiClient;
public MockApiClient(ApiClient apiClient)
{
_apiClient = apiClient;
}
public Task AddCategory(string category, string savePath)
{
return _apiClient.AddCategory(category, savePath);
}
public Task AddPeers(IEnumerable<string> hashes, IEnumerable<PeerId> peers)
{
return _apiClient.AddPeers(hashes, peers);
}
public Task AddTorrent(IEnumerable<string>? urls = null, Dictionary<string, Stream>? torrents = null, string? savePath = null, string? cookie = null, string? category = null, IEnumerable<string>? tags = null, bool? skipChecking = null, bool? paused = null, string? contentLayout = null, string? renameTorrent = null, long? uploadLimit = null, long? downloadLimit = null, float? ratioLimit = null, int? seedingTimeLimit = null, bool? autoTorrentManagement = null, bool? sequentialDownload = null, bool? firstLastPiecePriority = null)
{
return _apiClient.AddTorrent(urls, torrents, savePath, cookie, category, tags, skipChecking, paused, contentLayout, renameTorrent, uploadLimit, downloadLimit, ratioLimit, seedingTimeLimit, autoTorrentManagement, sequentialDownload, firstLastPiecePriority);
}
public Task AddTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes)
{
return _apiClient.AddTorrentTags(tags, all, hashes);
}
public Task AddTrackersToTorrent(string hash, IEnumerable<string> urls)
{
return _apiClient.AddTrackersToTorrent(hash, urls);
}
public Task BanPeers(IEnumerable<PeerId> peers)
{
return _apiClient.BanPeers(peers);
}
public Task<bool> CheckAuthState()
{
return _apiClient.CheckAuthState();
}
public Task CreateTags(IEnumerable<string> tags)
{
return _apiClient.CreateTags(tags);
}
public Task DecreaseTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.DecreaseTorrentPriority(all, hashes);
}
public Task DeleteTags(IEnumerable<string> tags)
{
return _apiClient.DeleteTags(tags);
}
public Task DeleteTorrents(bool? all = null, bool deleteFiles = false, params string[] hashes)
{
return _apiClient.DeleteTorrents(all, deleteFiles, hashes);
}
public Task EditCategory(string category, string savePath)
{
return _apiClient.EditCategory(category, savePath);
}
public Task EditTracker(string hash, string originalUrl, string newUrl)
{
return _apiClient.EditTracker(hash, originalUrl, newUrl);
}
public Task<IReadOnlyDictionary<string, Category>> GetAllCategories()
{
return _apiClient.GetAllCategories();
}
public Task<IReadOnlyList<string>> GetAllTags()
{
return _apiClient.GetAllTags();
}
public Task<bool> GetAlternativeSpeedLimitsState()
{
return _apiClient.GetAlternativeSpeedLimitsState();
}
public Task<string> GetAPIVersion()
{
return _apiClient.GetAPIVersion();
}
public Task<Preferences> GetApplicationPreferences()
{
return _apiClient.GetApplicationPreferences();
}
public Task<string> GetApplicationVersion()
{
return _apiClient.GetApplicationVersion();
}
public Task<BuildInfo> GetBuildInfo()
{
return _apiClient.GetBuildInfo();
}
public Task<string> GetDefaultSavePath()
{
return _apiClient.GetDefaultSavePath();
}
public Task<long> GetGlobalDownloadLimit()
{
return _apiClient.GetGlobalDownloadLimit();
}
public Task<GlobalTransferInfo> GetGlobalTransferInfo()
{
return _apiClient.GetGlobalTransferInfo();
}
public Task<long> GetGlobalUploadLimit()
{
return _apiClient.GetGlobalUploadLimit();
}
public Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null)
{
return _apiClient.GetLog(normal, info, warning, critical, lastKnownId);
}
public Task<MainData> GetMainData(int requestId)
{
return _apiClient.GetMainData(requestId);
}
public Task<IReadOnlyList<PeerLog>> GetPeerLog(int? lastKnownId = null)
{
return _apiClient.GetPeerLog(lastKnownId);
}
public Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes)
{
var list = new List<FileData>();
list.Add(new FileData(2, "slackware-14.2-iso/slackware-14.2-source-d6.iso", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(3, "slackware-14.2-iso/slackware-14.2-source-d6.iso.asc", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(4, "slackware-14.2-iso/slackware-14.2-source-d6.iso.md5", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(5, "slackware-14.2-iso/slackware-14.2-source-d6.iso.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(6, "slackware-14.2-iso/temp/slackware-14.2-source-d6.iso.md5", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(7, "slackware-14.2-iso/temp/slackware-14.2-source-d6.iso.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(8, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(9, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2.asc", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(10, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2.md5", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(11, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(12, "really/long/directory/path/is/here/file.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(13, "other.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
return Task.FromResult<IReadOnlyList<FileData>>(list);
}
public Task<IReadOnlyDictionary<string, long>> GetTorrentDownloadLimit(bool? all = null, params string[] hashes)
{
return _apiClient.GetTorrentDownloadLimit(all, hashes);
}
public Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, params string[] hashes)
{
return _apiClient.GetTorrentList(filter, category, tag, sort, reverse, limit, offset, hashes);
}
public Task<TorrentPeers> GetTorrentPeersData(string hash, int requestId)
{
return _apiClient.GetTorrentPeersData(hash, requestId);
}
public Task<IReadOnlyList<string>> GetTorrentPieceHashes(string hash)
{
return _apiClient.GetTorrentPieceHashes(hash);
}
public Task<IReadOnlyList<PieceState>> GetTorrentPieceStates(string hash)
{
return _apiClient.GetTorrentPieceStates(hash);
}
public Task<TorrentProperties> GetTorrentProperties(string hash)
{
return _apiClient.GetTorrentProperties(hash);
}
public Task<IReadOnlyList<TorrentTrackers>> GetTorrentTrackers(string hash)
{
return _apiClient.GetTorrentTrackers(hash);
}
public Task<IReadOnlyDictionary<string, long>> GetTorrentUploadLimit(bool? all = null, params string[] hashes)
{
return _apiClient.GetTorrentUploadLimit(all, hashes);
}
public Task<IReadOnlyList<WebSeed>> GetTorrentWebSeeds(string hash)
{
return _apiClient.GetTorrentWebSeeds(hash);
}
public Task IncreaseTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.IncreaseTorrentPriority(all, hashes);
}
public Task Login(string username, string password)
{
return _apiClient.Login(username, password);
}
public Task Logout()
{
return _apiClient.Logout();
}
public Task MaximalTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.MaximalTorrentPriority(all, hashes);
}
public Task MinimalTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.MinimalTorrentPriority(all, hashes);
}
public Task PauseTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.PauseTorrents(all, hashes);
}
public Task ReannounceTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.ReannounceTorrents(all, hashes);
}
public Task RecheckTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.ReannounceTorrents(all, hashes);
}
public Task RemoveCategories(params string[] categories)
{
return _apiClient.RemoveCategories(categories);
}
public Task RemoveTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes)
{
return _apiClient.RemoveTorrentTags(tags, all, hashes);
}
public Task RemoveTrackers(string hash, IEnumerable<string> urls)
{
return _apiClient.RemoveTrackers(hash, urls);
}
public Task RenameFile(string hash, string oldPath, string newPath)
{
return _apiClient.RenameFile(hash, oldPath, newPath);
}
public Task RenameFolder(string hash, string oldPath, string newPath)
{
return _apiClient.RenameFolder(hash, oldPath, newPath);
}
public Task ResumeTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.ResumeTorrents(all, hashes);
}
public Task SetApplicationPreferences(UpdatePreferences preferences)
{
return _apiClient.SetApplicationPreferences(preferences);
}
public Task SetAutomaticTorrentManagement(bool enable, bool? all = null, params string[] hashes)
{
return _apiClient.SetAutomaticTorrentManagement(enable, all, hashes);
}
public Task SetFilePriority(string hash, IEnumerable<int> id, Priority priority)
{
return _apiClient.SetFilePriority(hash, id, priority);
}
public Task SetFirstLastPiecePriority(bool? all = null, params string[] hashes)
{
return _apiClient.SetFirstLastPiecePriority(all, hashes);
}
public Task SetForceStart(bool value, bool? all = null, params string[] hashes)
{
return _apiClient.SetForceStart(value, all, hashes);
}
public Task SetGlobalDownloadLimit(long limit)
{
return _apiClient.SetGlobalDownloadLimit(limit);
}
public Task SetGlobalUploadLimit(long limit)
{
return _apiClient.SetGlobalDownloadLimit(limit);
}
public Task SetSuperSeeding(bool value, bool? all = null, params string[] hashes)
{
return _apiClient.SetSuperSeeding(value, all, hashes);
}
public Task SetTorrentCategory(string category, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentCategory(category, all, hashes);
}
public Task SetTorrentDownloadLimit(long limit, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentDownloadLimit(limit, all, hashes);
}
public Task SetTorrentLocation(string location, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentLocation(location, all, hashes);
}
public Task SetTorrentName(string name, string hash)
{
return _apiClient.SetTorrentName(name, hash);
}
public Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentShareLimit(ratioLimit, seedingTimeLimit, all, hashes);
}
public Task SetTorrentUploadLimit(long limit, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentUploadLimit(limit, all, hashes);
}
public Task Shutdown()
{
return _apiClient.Shutdown();
}
public Task ToggleAlternativeSpeedLimits()
{
return _apiClient.ToggleAlternativeSpeedLimits();
}
public Task ToggleSequentialDownload(bool? all = null, params string[] hashes)
{
return _apiClient.ToggleSequentialDownload(all, hashes);
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record BuildInfo
{
[JsonConstructor]
public BuildInfo(
string qTVersion,
string libTorrentVersion,
string boostVersion,
string openSSLVersion,
int bitness)
{
QTVersion = qTVersion;
LibTorrentVersion = libTorrentVersion;
BoostVersion = boostVersion;
OpenSSLVersion = openSSLVersion;
Bitness = bitness;
}
[JsonPropertyName("qt")]
public string QTVersion { get; }
[JsonPropertyName("libtorrent")]
public string LibTorrentVersion { get; }
[JsonPropertyName("boost")]
public string BoostVersion { get; }
[JsonPropertyName("openssl")]
public string OpenSSLVersion { get; }
[JsonPropertyName("bitness")]
public int Bitness { get; }
}
}

View File

@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record Category
{
[JsonConstructor]
public Category(
string name,
string? savePath)
{
Name = name;
SavePath = savePath;
}
[JsonPropertyName("name")]
public string Name { get; }
[JsonPropertyName("savePath")]
public string? SavePath { get; }
}
}

View File

@@ -0,0 +1,52 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record FileData
{
[JsonConstructor]
public FileData(
int index,
string name,
long size,
float progress,
Priority priority,
bool isSeed,
IReadOnlyList<int> pieceRange,
float availability)
{
Index = index;
Name = name;
Size = size;
Progress = progress;
Priority = priority;
IsSeed = isSeed;
PieceRange = pieceRange ?? [];
Availability = availability;
}
[JsonPropertyName("index")]
public int Index { get; }
[JsonPropertyName("name")]
public string Name { get; }
[JsonPropertyName("size")]
public long Size { get; }
[JsonPropertyName("progress")]
public float Progress { get; }
[JsonPropertyName("priority")]
public Priority Priority { get; }
[JsonPropertyName("is_seed")]
public bool IsSeed { get; }
[JsonPropertyName("piece_range")]
public IReadOnlyList<int> PieceRange { get; }
[JsonPropertyName("availability")]
public float Availability { get; }
}
}

View File

@@ -0,0 +1,52 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record GlobalTransferInfo
{
[JsonConstructor]
public GlobalTransferInfo(
string? connectionStatus,
int? dHTNodes,
long? downloadInfoData,
long? downloadInfoSpeed,
long? downloadRateLimit,
long? uploadInfoData,
long? uploadInfoSpeed,
long? uploadRateLimit)
{
ConnectionStatus = connectionStatus;
DHTNodes = dHTNodes;
DownloadInfoData = downloadInfoData;
DownloadInfoSpeed = downloadInfoSpeed;
DownloadRateLimit = downloadRateLimit;
UploadInfoData = uploadInfoData;
UploadInfoSpeed = uploadInfoSpeed;
UploadRateLimit = uploadRateLimit;
}
[JsonPropertyName("connection_status")]
public string? ConnectionStatus { get; }
[JsonPropertyName("dht_nodes")]
public int? DHTNodes { get; }
[JsonPropertyName("dl_info_data")]
public long? DownloadInfoData { get; }
[JsonPropertyName("dl_info_speed")]
public long? DownloadInfoSpeed { get; }
[JsonPropertyName("dl_rate_limit")]
public long? DownloadRateLimit { get; }
[JsonPropertyName("up_info_data")]
public long? UploadInfoData { get; }
[JsonPropertyName("up_info_speed")]
public long? UploadInfoSpeed { get; }
[JsonPropertyName("up_rate_limit")]
public long? UploadRateLimit { get; }
}
}

View File

@@ -0,0 +1,32 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record Log
{
[JsonConstructor]
public Log(
int id,
string message,
long timestamp,
LogType type)
{
Id = id;
Message = message;
Timestamp = timestamp;
Type = type;
}
[JsonPropertyName("id")]
public int Id { get; }
[JsonPropertyName("message")]
public string Message { get; }
[JsonPropertyName("timestamp")]
public long Timestamp { get; }
[JsonPropertyName("type")]
public LogType Type { get; }
}
}

View File

@@ -0,0 +1,10 @@
namespace Lantean.QBitTorrentClient.Models
{
public enum LogType
{
Normal = 1,
Info = 2,
Warning = 4,
Critical = 8
}
}

View File

@@ -0,0 +1,65 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record MainData
{
[JsonConstructor]
public MainData(
int responseId,
bool fullUpdate,
IReadOnlyDictionary<string, Torrent>? torrents,
IReadOnlyList<string>? torrentsRemoved,
IReadOnlyDictionary<string, Category>? categories,
IReadOnlyList<string>? categoriesRemoved,
IReadOnlyList<string>? tags,
IReadOnlyList<string>? tagsRemoved,
IReadOnlyDictionary<string, IReadOnlyList<string>> trackers,
ServerState? serverState)
{
ResponseId = responseId;
FullUpdate = fullUpdate;
Torrents = torrents;
TorrentsRemoved = torrentsRemoved;
Categories = categories;
CategoriesRemoved = categoriesRemoved;
Tags = tags;
TagsRemoved = tagsRemoved;
Trackers = trackers;
ServerState = serverState;
}
[JsonPropertyName("rid")]
public int ResponseId { get; }
[JsonPropertyName("full_update")]
public bool FullUpdate { get; }
[JsonPropertyName("torrents")]
public IReadOnlyDictionary<string, Torrent>? Torrents { get; }
[JsonPropertyName("torrents_removed")]
public IReadOnlyList<string>? TorrentsRemoved { get; }
[JsonPropertyName("categories")]
public IReadOnlyDictionary<string, Category>? Categories { get; }
[JsonPropertyName("categories_removed")]
public IReadOnlyList<string>? CategoriesRemoved { get; }
[JsonPropertyName("tags")]
public IReadOnlyList<string>? Tags { get; }
[JsonPropertyName("tags_removed")]
public IReadOnlyList<string>? TagsRemoved { get; }
[JsonPropertyName("trackers")]
public IReadOnlyDictionary<string, IReadOnlyList<string>>? Trackers { get; }
[JsonPropertyName("trackers_removed")]
public IReadOnlyList<string>? TrackersRemoved { get; }
[JsonPropertyName("server_state")]
public ServerState? ServerState { get; }
}
}

View File

@@ -0,0 +1,92 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record Peer
{
[JsonConstructor]
public Peer(
string? client,
string? connection,
string? country,
string? countryCode,
long? downloadSpeed,
long? downloaded,
string? files,
string? flags,
string? flagsDescription,
string? iPAddress,
string? clientId,
int? port,
float? progress,
float? relevance,
long? uploadSpeed,
long? uploaded)
{
Client = client;
Connection = connection;
Country = country;
CountryCode = countryCode;
DownloadSpeed = downloadSpeed;
Downloaded = downloaded;
Files = files;
Flags = flags;
FlagsDescription = flagsDescription;
IPAddress = iPAddress;
ClientId = clientId;
Port = port;
Progress = progress;
Relevance = relevance;
UploadSpeed = uploadSpeed;
Uploaded = uploaded;
}
[JsonPropertyName("client")]
public string? Client { get; }
[JsonPropertyName("connection")]
public string? Connection { get; }
[JsonPropertyName("country")]
public string? Country { get; }
[JsonPropertyName("country_code")]
public string? CountryCode { get; }
[JsonPropertyName("dl_speed")]
public long? DownloadSpeed { get; }
[JsonPropertyName("downloaded")]
public long? Downloaded { get; }
[JsonPropertyName("files")]
public string? Files { get; }
[JsonPropertyName("flags")]
public string? Flags { get; }
[JsonPropertyName("flags_desc")]
public string? FlagsDescription { get; }
[JsonPropertyName("ip")]
public string? IPAddress { get; }
[JsonPropertyName("peer_id_client")]
public string? ClientId { get; }
[JsonPropertyName("port")]
public int? Port { get; }
[JsonPropertyName("progress")]
public float? Progress { get; }
[JsonPropertyName("relevance")]
public float? Relevance { get; }
[JsonPropertyName("up_speed")]
public long? UploadSpeed { get; }
[JsonPropertyName("uploaded")]
public long? Uploaded { get; }
}
}

View File

@@ -0,0 +1,14 @@
namespace Lantean.QBitTorrentClient.Models
{
public readonly struct PeerId(string host, int port)
{
public string Host { get; } = host;
public int Port { get; } = port;
public override string ToString()
{
return $"{Host}:{Port}";
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record PeerLog
{
[JsonConstructor]
public PeerLog(
int id,
string iPAddress,
long timestamp,
bool blocked,
string reason)
{
Id = id;
IPAddress = iPAddress;
Timestamp = timestamp;
Blocked = blocked;
Reason = reason;
}
[JsonPropertyName("id")]
public int Id { get; }
[JsonPropertyName("ip")]
public string IPAddress { get; }
[JsonPropertyName("timestamp")]
public long Timestamp { get; }
[JsonPropertyName("blocked")]
public bool Blocked { get; }
[JsonPropertyName("reason")]
public string Reason { get; }
}
}

View File

@@ -0,0 +1,9 @@
namespace Lantean.QBitTorrentClient.Models
{
public enum PieceState
{
NotDownloaded = 0,
Downloading = 1,
Downloaded = 2,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
namespace Lantean.QBitTorrentClient.Models
{
public enum Priority
{
DoNotDownload = 0,
Normal = 1,
High = 6,
Maximum = 7
}
}

View File

@@ -0,0 +1,82 @@

namespace Lantean.QBitTorrentClient.Models
{
public class SaveLocation
{
public bool IsWatchedFolder { get; set; }
public bool IsDefaltFolder { get; set; }
public string? SavePath { get; set; }
public static SaveLocation Create(object? value)
{
if (value is int intValue)
{
if (intValue == 0)
{
return new SaveLocation
{
IsWatchedFolder = true
};
}
else if (intValue == 1)
{
return new SaveLocation
{
IsDefaltFolder = true
};
}
}
else if (value is string stringValue)
{
if (stringValue == "0")
{
return new SaveLocation
{
IsWatchedFolder = true
};
}
else if (stringValue == "1")
{
return new SaveLocation
{
IsDefaltFolder = true
};
}
else
{
return new SaveLocation
{
SavePath = stringValue
};
}
}
throw new ArgumentOutOfRangeException(nameof(value));
}
public object ToValue()
{
if (IsWatchedFolder)
{
return 0;
}
else if (IsDefaltFolder)
{
return 1;
}
else if (SavePath is not null)
{
return SavePath;
}
throw new InvalidOperationException("Invalid value.");
}
public override string? ToString()
{
return ToValue().ToString();
}
}
}

View File

@@ -0,0 +1,105 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record ServerState : GlobalTransferInfo
{
[JsonConstructor]
public ServerState(
long? allTimeDownloaded,
long? allTimeUploaded,
int? averageTimeQueue,
string? connectionStatus,
int? dHTNodes,
long? downloadInfoData,
long? downloadInfoSpeed,
long? downloadRateLimit,
long? freeSpaceOnDisk,
float? globalRatio,
int? queuedIOJobs,
bool? queuing,
float? readCacheHits,
float? readCacheOverload,
int? refreshInterval,
int? totalBuffersSize,
int? totalPeerConnections,
int? totalQueuedSize,
long? totalWastedSession,
long? uploadInfoData,
long? uploadInfoSpeed,
long? uploadRateLimit,
bool? useAltSpeedLimits,
bool? useSubcategories,
float? writeCacheOverload) : base(connectionStatus, dHTNodes, downloadInfoData, downloadInfoSpeed, downloadRateLimit, uploadInfoData, uploadInfoSpeed, uploadRateLimit)
{
AllTimeDownloaded = allTimeDownloaded;
AllTimeUploaded = allTimeUploaded;
AverageTimeQueue = averageTimeQueue;
FreeSpaceOnDisk = freeSpaceOnDisk;
GlobalRatio = globalRatio;
QueuedIOJobs = queuedIOJobs;
Queuing = queuing;
ReadCacheHits = readCacheHits;
ReadCacheOverload = readCacheOverload;
RefreshInterval = refreshInterval;
TotalBuffersSize = totalBuffersSize;
TotalPeerConnections = totalPeerConnections;
TotalQueuedSize = totalQueuedSize;
TotalWastedSession = totalWastedSession;
UseAltSpeedLimits = useAltSpeedLimits;
UseSubcategories = useSubcategories;
WriteCacheOverload = writeCacheOverload;
}
[JsonPropertyName("alltime_dl")]
public long? AllTimeDownloaded { get; }
[JsonPropertyName("alltime_ul")]
public long? AllTimeUploaded { get; }
[JsonPropertyName("average_time_queue")]
public int? AverageTimeQueue { get; }
[JsonPropertyName("free_space_on_disk")]
public long? FreeSpaceOnDisk { get; }
[JsonPropertyName("global_ratio")]
public float? GlobalRatio { get; }
[JsonPropertyName("queued_io_jobs")]
public int? QueuedIOJobs { get; }
[JsonPropertyName("queueing")]
public bool? Queuing { get; }
[JsonPropertyName("read_cache_hits")]
public float? ReadCacheHits { get; }
[JsonPropertyName("read_cache_overload")]
public float? ReadCacheOverload { get; }
[JsonPropertyName("refresh_interval")]
public int? RefreshInterval { get; }
[JsonPropertyName("total_buffers_size")]
public int? TotalBuffersSize { get; }
[JsonPropertyName("total_peer_connections")]
public int? TotalPeerConnections { get; }
[JsonPropertyName("total_queued_size")]
public int? TotalQueuedSize { get; }
[JsonPropertyName("total_wasted_session")]
public long? TotalWastedSession { get; }
[JsonPropertyName("use_alt_speed_limits")]
public bool? UseAltSpeedLimits { get; }
[JsonPropertyName("use_subcategories")]
public bool? UseSubcategories { get; }
[JsonPropertyName("write_cache_overload")]
public float? WriteCacheOverload { get; }
}
}

View File

@@ -0,0 +1,249 @@
using Lantean.QBitTorrentClient.Converters;
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record Torrent
{
[JsonConstructor]
public Torrent(
long? addedOn,
long? amountLeft,
bool? automaticTorrentManagement,
float? availability,
string? category,
long? completed,
long? completionOn,
string? contentPath,
long? downloadLimit,
long? downloadSpeed,
long? downloaded,
long? downloadedSession,
long? estimatedTimeOfArrival,
bool? firstLastPiecePriority,
bool? forceStart,
string? infoHashV1,
string? infoHashV2,
long? lastActivity,
string? magnetUri,
float? maxRatio,
int? maxSeedingTime,
string? name,
int? numberComplete,
int? numberIncomplete,
int? numberLeeches,
int? numberSeeds,
int? priority,
float? progress,
float? ratio,
float? ratioLimit,
string? savePath,
long? seedingTime,
int? seedingTimeLimit,
long? seenComplete,
bool? sequentialDownload,
long? size,
string? state,
bool? superSeeding,
IReadOnlyList<string>? tags,
int? timeActive,
long? totalSize,
string? tracker,
long? uploadLimit,
long? uploaded,
long? uploadedSession,
long? uploadSpeed,
long? reannounce)
{
AddedOn = addedOn;
AmountLeft = amountLeft;
AutomaticTorrentManagement = automaticTorrentManagement;
Availability = availability;
Category = category;
Completed = completed;
CompletionOn = completionOn;
ContentPath = contentPath;
DownloadLimit = downloadLimit;
DownloadSpeed = downloadSpeed;
Downloaded = downloaded;
DownloadedSession = downloadedSession;
EstimatedTimeOfArrival = estimatedTimeOfArrival;
FirstLastPiecePriority = firstLastPiecePriority;
ForceStart = forceStart;
InfoHashV1 = infoHashV1;
InfoHashV2 = infoHashV2;
LastActivity = lastActivity;
MagnetUri = magnetUri;
MaxRatio = maxRatio;
MaxSeedingTime = maxSeedingTime;
Name = name;
NumberComplete = numberComplete;
NumberIncomplete = numberIncomplete;
NumberLeeches = numberLeeches;
NumberSeeds = numberSeeds;
Priority = priority;
Progress = progress;
Ratio = ratio;
RatioLimit = ratioLimit;
SavePath = savePath;
SeedingTime = seedingTime;
SeedingTimeLimit = seedingTimeLimit;
SeenComplete = seenComplete;
SequentialDownload = sequentialDownload;
Size = size;
State = state;
SuperSeeding = superSeeding;
Tags = tags ?? [];
TimeActive = timeActive;
TotalSize = totalSize;
Tracker = tracker;
UploadLimit = uploadLimit;
Uploaded = uploaded;
UploadedSession = uploadedSession;
UploadSpeed = uploadSpeed;
Reannounce = reannounce;
}
[JsonPropertyName("added_on")]
public long? AddedOn { get; }
[JsonPropertyName("amount_left")]
public long? AmountLeft { get; }
[JsonPropertyName("auto_tmm")]
public bool? AutomaticTorrentManagement { get; }
[JsonPropertyName("availability")]
public float? Availability { get; }
[JsonPropertyName("category")]
public string? Category { get; }
[JsonPropertyName("completed")]
public long? Completed { get; }
[JsonPropertyName("completion_on")]
public long? CompletionOn { get; }
[JsonPropertyName("content_path")]
public string? ContentPath { get; }
[JsonPropertyName("dl_limit")]
public long? DownloadLimit { get; }
[JsonPropertyName("dlspeed")]
public long? DownloadSpeed { get; }
[JsonPropertyName("downloaded")]
public long? Downloaded { get; }
[JsonPropertyName("downloaded_session")]
public long? DownloadedSession { get; }
[JsonPropertyName("eta")]
public long? EstimatedTimeOfArrival { get; }
[JsonPropertyName("f_l_piece_prio")]
public bool? FirstLastPiecePriority { get; }
[JsonPropertyName("force_start")]
public bool? ForceStart { get; }
[JsonPropertyName("infohash_v1")]
public string? InfoHashV1 { get; }
[JsonPropertyName("infohash_v2")]
public string? InfoHashV2 { get; }
[JsonPropertyName("last_activity")]
public long? LastActivity { get; }
[JsonPropertyName("magnet_uri")]
public string? MagnetUri { get; }
[JsonPropertyName("max_ratio")]
public float? MaxRatio { get; }
[JsonPropertyName("max_seeding_time")]
public int? MaxSeedingTime { get; }
[JsonPropertyName("name")]
public string? Name { get; }
[JsonPropertyName("num_complete")]
public int? NumberComplete { get; }
[JsonPropertyName("num_incomplete")]
public int? NumberIncomplete { get; }
[JsonPropertyName("num_leechs")]
public int? NumberLeeches { get; }
[JsonPropertyName("num_seeds")]
public int? NumberSeeds { get; }
[JsonPropertyName("priority")]
public int? Priority { get; }
[JsonPropertyName("progress")]
public float? Progress { get; }
[JsonPropertyName("ratio")]
public float? Ratio { get; }
[JsonPropertyName("ratio_limit")]
public float? RatioLimit { get; }
[JsonPropertyName("save_path")]
public string? SavePath { get; }
[JsonPropertyName("seeding_time")]
public long? SeedingTime { get; }
[JsonPropertyName("seeding_time_limit")]
public int? SeedingTimeLimit { get; }
[JsonPropertyName("seen_complete")]
public long? SeenComplete { get; }
[JsonPropertyName("seq_dl")]
public bool? SequentialDownload { get; }
[JsonPropertyName("size")]
public long? Size { get; }
[JsonPropertyName("state")]
public string? State { get; }
[JsonPropertyName("super_seeding")]
public bool? SuperSeeding { get; }
[JsonPropertyName("tags")]
[JsonConverter(typeof(CommaSeparatedJsonConverter))]
public IReadOnlyList<string>? Tags { get; }
[JsonPropertyName("time_active")]
public int? TimeActive { get; }
[JsonPropertyName("total_size")]
public long? TotalSize { get; }
[JsonPropertyName("tracker")]
public string? Tracker { get; }
[JsonPropertyName("up_limit")]
public long? UploadLimit { get; }
[JsonPropertyName("uploaded")]
public long? Uploaded { get; }
[JsonPropertyName("uploaded_session")]
public long? UploadedSession { get; }
[JsonPropertyName("upspeed")]
public long? UploadSpeed { get; }
[JsonPropertyName("reannounce")]
public long? Reannounce { get; }
}
}

View File

@@ -0,0 +1,37 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record TorrentPeers
{
[JsonConstructor]
public TorrentPeers(
bool fullUpdate,
IReadOnlyDictionary<string, Peer>? peers,
IReadOnlyList<string>? peersRemoved,
int requestId,
bool? showFlags)
{
FullUpdate = fullUpdate;
Peers = peers;
PeersRemoved = peersRemoved;
RequestId = requestId;
ShowFlags = showFlags;
}
[JsonPropertyName("full_update")]
public bool FullUpdate { get; }
[JsonPropertyName("peers")]
public IReadOnlyDictionary<string, Peer>? Peers { get; }
[JsonPropertyName("peers_removed")]
public IReadOnlyList<string>? PeersRemoved { get; }
[JsonPropertyName("rid")]
public int RequestId { get; }
[JsonPropertyName("show_flags")]
public bool? ShowFlags { get; }
}
}

View File

@@ -0,0 +1,187 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record TorrentProperties
{
[JsonConstructor]
public TorrentProperties(
long additionDate,
string comment,
long completionDate,
string createdBy,
long creationDate,
long downloadLimit,
long downloadSpeed,
long downloadSpeedAverage,
int estimatedTimeOfArrival,
long lastSeen,
int connections,
int connectionsLimit,
int peers,
int peersTotal,
int pieceSize,
int piecesHave,
int piecesNum,
int reannounce,
string savePath,
int seedingTime,
int seeds,
int seedsTotal,
float shareRatio,
int timeElapsed,
long totalDownloaded,
long totalDownloadedSession,
long totalSize,
long totalUploaded,
long totalUploadedSession,
long totalWasted,
long uploadLimit,
long uploadSpeed,
long uploadSpeedAverage,
string infoHashV1,
string infoHashV2)
{
AdditionDate = additionDate;
Comment = comment;
CompletionDate = completionDate;
CreatedBy = createdBy;
CreationDate = creationDate;
DownloadLimit = downloadLimit;
DownloadSpeed = downloadSpeed;
DownloadSpeedAverage = downloadSpeedAverage;
EstimatedTimeOfArrival = estimatedTimeOfArrival;
LastSeen = lastSeen;
Connections = connections;
ConnectionsLimit = connectionsLimit;
Peers = peers;
PeersTotal = peersTotal;
PieceSize = pieceSize;
PiecesHave = piecesHave;
PiecesNum = piecesNum;
Reannounce = reannounce;
SavePath = savePath;
SeedingTime = seedingTime;
Seeds = seeds;
SeedsTotal = seedsTotal;
ShareRatio = shareRatio;
TimeElapsed = timeElapsed;
TotalDownloaded = totalDownloaded;
TotalDownloadedSession = totalDownloadedSession;
TotalSize = totalSize;
TotalUploaded = totalUploaded;
TotalUploadedSession = totalUploadedSession;
TotalWasted = totalWasted;
UploadLimit = uploadLimit;
UploadSpeed = uploadSpeed;
UploadSpeedAverage = uploadSpeedAverage;
InfoHashV1 = infoHashV1;
InfoHashV2 = infoHashV2;
}
[JsonPropertyName("addition_date")]
public long AdditionDate { get; }
[JsonPropertyName("comment")]
public string Comment { get; }
[JsonPropertyName("completion_date")]
public long CompletionDate { get; }
[JsonPropertyName("created_by")]
public string CreatedBy { get; }
[JsonPropertyName("creation_date")]
public long CreationDate { get; }
[JsonPropertyName("dl_limit")]
public long DownloadLimit { get; }
[JsonPropertyName("dl_speed")]
public long DownloadSpeed { get; }
[JsonPropertyName("dl_speed_avg")]
public long DownloadSpeedAverage { get; }
[JsonPropertyName("eta")]
public int EstimatedTimeOfArrival { get; }
[JsonPropertyName("last_seen")]
public long LastSeen { get; }
[JsonPropertyName("nb_connections")]
public int Connections { get; }
[JsonPropertyName("nb_connections_limit")]
public int ConnectionsLimit { get; }
[JsonPropertyName("peers")]
public int Peers { get; }
[JsonPropertyName("peers_total")]
public int PeersTotal { get; }
[JsonPropertyName("piece_size")]
public int PieceSize { get; }
[JsonPropertyName("pieces_have")]
public int PiecesHave { get; }
[JsonPropertyName("pieces_num")]
public int PiecesNum { get; }
[JsonPropertyName("reannounce")]
public int Reannounce { get; }
[JsonPropertyName("save_path")]
public string SavePath { get; }
[JsonPropertyName("seeding_time")]
public int SeedingTime { get; }
[JsonPropertyName("seeds")]
public int Seeds { get; }
[JsonPropertyName("seeds_total")]
public int SeedsTotal { get; }
[JsonPropertyName("share_ratio")]
public float ShareRatio { get; }
[JsonPropertyName("time_elapsed")]
public int TimeElapsed { get; }
[JsonPropertyName("total_downloaded")]
public long TotalDownloaded { get; }
[JsonPropertyName("total_downloaded_session")]
public long TotalDownloadedSession { get; }
[JsonPropertyName("total_size")]
public long TotalSize { get; }
[JsonPropertyName("total_uploaded")]
public long TotalUploaded { get; }
[JsonPropertyName("total_uploaded_session")]
public long TotalUploadedSession { get; }
[JsonPropertyName("total_wasted")]
public long TotalWasted { get; }
[JsonPropertyName("up_limit")]
public long UploadLimit { get; }
[JsonPropertyName("up_speed")]
public long UploadSpeed { get; }
[JsonPropertyName("up_speed_avg")]
public long UploadSpeedAverage { get; }
[JsonPropertyName("infohash_v1")]
public string InfoHashV1 { get; }
[JsonPropertyName("infohash_v2")]
public string InfoHashV2 { get; }
}
}

View File

@@ -0,0 +1,52 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record TorrentTrackers
{
[JsonConstructor]
public TorrentTrackers(
string url,
TrackerStatus status,
int tier,
int peers,
int seeds,
int leeches,
int downloads,
string message)
{
Url = url;
Status = status;
Tier = tier;
Peers = peers;
Seeds = seeds;
Leeches = leeches;
Downloads = downloads;
Message = message;
}
[JsonPropertyName("url")]
public string Url { get; }
[JsonPropertyName("status")]
public TrackerStatus Status { get; }
[JsonPropertyName("tier")]
public int Tier { get; }
[JsonPropertyName("num_peers")]
public int Peers { get; }
[JsonPropertyName("num_seeds")]
public int Seeds { get; }
[JsonPropertyName("num_leeches")]
public int Leeches { get; }
[JsonPropertyName("num_downloaded")]
public int Downloads { get; }
[JsonPropertyName("msg")]
public string Message { get; }
}
}

View File

@@ -0,0 +1,11 @@
namespace Lantean.QBitTorrentClient.Models
{
public enum TrackerStatus
{
Disabled = 0,
Uncontacted = 1,
Working = 2,
Updating = 3,
NotWorking = 4,
}
}

View File

@@ -0,0 +1,613 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record UpdatePreferences
{
[JsonPropertyName("add_to_top_of_queue")]
public bool? AddToTopOfQueue { get; set; }
[JsonPropertyName("add_trackers")]
public string? AddTrackers { get; set; }
[JsonPropertyName("add_trackers_enabled")]
public bool? AddTrackersEnabled { get; set; }
[JsonPropertyName("alt_dl_limit")]
public int? AltDlLimit { get; set; }
[JsonPropertyName("alt_up_limit")]
public int? AltUpLimit { get; set; }
[JsonPropertyName("alternative_webui_enabled")]
public bool? AlternativeWebuiEnabled { get; set; }
[JsonPropertyName("alternative_webui_path")]
public string? AlternativeWebuiPath { get; set; }
[JsonPropertyName("announce_ip")]
public string? AnnounceIp { get; set; }
[JsonPropertyName("announce_to_all_tiers")]
public bool? AnnounceToAllTiers { get; set; }
[JsonPropertyName("announce_to_all_trackers")]
public bool? AnnounceToAllTrackers { get; set; }
[JsonPropertyName("anonymous_mode")]
public bool? AnonymousMode { get; set; }
[JsonPropertyName("async_io_threads")]
public int? AsyncIoThreads { get; set; }
[JsonPropertyName("auto_delete_mode")]
public int? AutoDeleteMode { get; set; }
[JsonPropertyName("auto_tmm_enabled")]
public bool? AutoTmmEnabled { get; set; }
[JsonPropertyName("autorun_enabled")]
public bool? AutorunEnabled { get; set; }
[JsonPropertyName("autorun_on_torrent_added_enabled")]
public bool? AutorunOnTorrentAddedEnabled { get; set; }
[JsonPropertyName("autorun_on_torrent_added_program")]
public string? AutorunOnTorrentAddedProgram { get; set; }
[JsonPropertyName("autorun_program")]
public string? AutorunProgram { get; set; }
[JsonPropertyName("banned_IPs")]
public string? BannedIPs { get; set; }
[JsonPropertyName("bdecode_depth_limit")]
public int? BdecodeDepthLimit { get; set; }
[JsonPropertyName("bdecode_token_limit")]
public int? BdecodeTokenLimit { get; set; }
[JsonPropertyName("bittorrent_protocol")]
public int? BittorrentProtocol { get; set; }
[JsonPropertyName("block_peers_on_privileged_ports")]
public bool? BlockPeersOnPrivilegedPorts { get; set; }
[JsonPropertyName("bypass_auth_subnet_whitelist")]
public string? BypassAuthSubnetWhitelist { get; set; }
[JsonPropertyName("bypass_auth_subnet_whitelist_enabled")]
public bool? BypassAuthSubnetWhitelistEnabled { get; set; }
[JsonPropertyName("bypass_local_auth")]
public bool? BypassLocalAuth { get; set; }
[JsonPropertyName("category_changed_tmm_enabled")]
public bool? CategoryChangedTmmEnabled { get; set; }
[JsonPropertyName("checking_memory_use")]
public int? CheckingMemoryUse { get; set; }
[JsonPropertyName("connection_speed")]
public int? ConnectionSpeed { get; set; }
[JsonPropertyName("current_interface_address")]
public string? CurrentInterfaceAddress { get; set; }
[JsonPropertyName("current_interface_name")]
public string? CurrentInterfaceName { get; set; }
[JsonPropertyName("current_network_interface")]
public string? CurrentNetworkInterface { get; set; }
[JsonPropertyName("dht")]
public bool? Dht { get; set; }
[JsonPropertyName("disk_cache")]
public int? DiskCache { get; set; }
[JsonPropertyName("disk_cache_ttl")]
public int? DiskCacheTtl { get; set; }
[JsonPropertyName("disk_io_read_mode")]
public int? DiskIoReadMode { get; set; }
[JsonPropertyName("disk_io_type")]
public int? DiskIoType { get; set; }
[JsonPropertyName("disk_io_write_mode")]
public int? DiskIoWriteMode { get; set; }
[JsonPropertyName("disk_queue_size")]
public int? DiskQueueSize { get; set; }
[JsonPropertyName("dl_limit")]
public int? DlLimit { get; set; }
[JsonPropertyName("dont_count_slow_torrents")]
public bool? DontCountSlowTorrents { get; set; }
[JsonPropertyName("dyndns_domain")]
public string? DyndnsDomain { get; set; }
[JsonPropertyName("dyndns_enabled")]
public bool? DyndnsEnabled { get; set; }
[JsonPropertyName("dyndns_password")]
public string? DyndnsPassword { get; set; }
[JsonPropertyName("dyndns_service")]
public int? DyndnsService { get; set; }
[JsonPropertyName("dyndns_username")]
public string? DyndnsUsername { get; set; }
[JsonPropertyName("embedded_tracker_port")]
public int? EmbeddedTrackerPort { get; set; }
[JsonPropertyName("embedded_tracker_port_forwarding")]
public bool? EmbeddedTrackerPortForwarding { get; set; }
[JsonPropertyName("enable_coalesce_read_write")]
public bool? EnableCoalesceReadWrite { get; set; }
[JsonPropertyName("enable_embedded_tracker")]
public bool? EnableEmbeddedTracker { get; set; }
[JsonPropertyName("enable_multi_connections_from_same_ip")]
public bool? EnableMultiConnectionsFromSameIp { get; set; }
[JsonPropertyName("enable_piece_extent_affinity")]
public bool? EnablePieceExtentAffinity { get; set; }
[JsonPropertyName("enable_upload_suggestions")]
public bool? EnableUploadSuggestions { get; set; }
[JsonPropertyName("encryption")]
public int? Encryption { get; set; }
[JsonPropertyName("excluded_file_names")]
public string? ExcludedFileNames { get; set; }
[JsonPropertyName("excluded_file_names_enabled")]
public bool? ExcludedFileNamesEnabled { get; set; }
[JsonPropertyName("export_dir")]
public string? ExportDir { get; set; }
[JsonPropertyName("export_dir_fin")]
public string? ExportDirFin { get; set; }
[JsonPropertyName("file_log_age")]
public int? FileLogAge { get; set; }
[JsonPropertyName("file_log_age_type")]
public int? FileLogAgeType { get; set; }
[JsonPropertyName("file_log_backup_enabled")]
public bool? FileLogBackupEnabled { get; set; }
[JsonPropertyName("file_log_delete_old")]
public bool? FileLogDeleteOld { get; set; }
[JsonPropertyName("file_log_enabled")]
public bool? FileLogEnabled { get; set; }
[JsonPropertyName("file_log_max_size")]
public int? FileLogMaxSize { get; set; }
[JsonPropertyName("file_log_path")]
public string? FileLogPath { get; set; }
[JsonPropertyName("file_pool_size")]
public int? FilePoolSize { get; set; }
[JsonPropertyName("hashing_threads")]
public int? HashingThreads { get; set; }
[JsonPropertyName("i2p_address")]
public string? I2pAddress { get; set; }
[JsonPropertyName("i2p_enabled")]
public bool? I2pEnabled { get; set; }
[JsonPropertyName("i2p_inbound_length")]
public int? I2pInboundLength { get; set; }
[JsonPropertyName("i2p_inbound_quantity")]
public int? I2pInboundQuantity { get; set; }
[JsonPropertyName("i2p_mixed_mode")]
public bool? I2pMixedMode { get; set; }
[JsonPropertyName("i2p_outbound_length")]
public int? I2pOutboundLength { get; set; }
[JsonPropertyName("i2p_outbound_quantity")]
public int? I2pOutboundQuantity { get; set; }
[JsonPropertyName("i2p_port")]
public int? I2pPort { get; set; }
[JsonPropertyName("idn_support_enabled")]
public bool? IdnSupportEnabled { get; set; }
[JsonPropertyName("incomplete_files_ext")]
public bool? IncompleteFilesExt { get; set; }
[JsonPropertyName("ip_filter_enabled")]
public bool? IpFilterEnabled { get; set; }
[JsonPropertyName("ip_filter_path")]
public string? IpFilterPath { get; set; }
[JsonPropertyName("ip_filter_trackers")]
public bool? IpFilterTrackers { get; set; }
[JsonPropertyName("limit_lan_peers")]
public bool? LimitLanPeers { get; set; }
[JsonPropertyName("limit_tcp_overhead")]
public bool? LimitTcpOverhead { get; set; }
[JsonPropertyName("limit_utp_rate")]
public bool? LimitUtpRate { get; set; }
[JsonPropertyName("listen_port")]
public int? ListenPort { get; set; }
[JsonPropertyName("locale")]
public string? Locale { get; set; }
[JsonPropertyName("lsd")]
public bool? Lsd { get; set; }
[JsonPropertyName("mail_notification_auth_enabled")]
public bool? MailNotificationAuthEnabled { get; set; }
[JsonPropertyName("mail_notification_email")]
public string? MailNotificationEmail { get; set; }
[JsonPropertyName("mail_notification_enabled")]
public bool? MailNotificationEnabled { get; set; }
[JsonPropertyName("mail_notification_password")]
public string? MailNotificationPassword { get; set; }
[JsonPropertyName("mail_notification_sender")]
public string? MailNotificationSender { get; set; }
[JsonPropertyName("mail_notification_smtp")]
public string? MailNotificationSmtp { get; set; }
[JsonPropertyName("mail_notification_ssl_enabled")]
public bool? MailNotificationSslEnabled { get; set; }
[JsonPropertyName("mail_notification_username")]
public string? MailNotificationUsername { get; set; }
[JsonPropertyName("max_active_checking_torrents")]
public int? MaxActiveCheckingTorrents { get; set; }
[JsonPropertyName("max_active_downloads")]
public int? MaxActiveDownloads { get; set; }
[JsonPropertyName("max_active_torrents")]
public int? MaxActiveTorrents { get; set; }
[JsonPropertyName("max_active_uploads")]
public int? MaxActiveUploads { get; set; }
[JsonPropertyName("max_concurrent_http_announces")]
public int? MaxConcurrentHttpAnnounces { get; set; }
[JsonPropertyName("max_connec")]
public int? MaxConnec { get; set; }
[JsonPropertyName("max_connec_per_torrent")]
public int? MaxConnecPerTorrent { get; set; }
[JsonPropertyName("max_inactive_seeding_time")]
public int? MaxInactiveSeedingTime { get; set; }
[JsonPropertyName("max_inactive_seeding_time_enabled")]
public bool? MaxInactiveSeedingTimeEnabled { get; set; }
[JsonPropertyName("max_ratio")]
public int? MaxRatio { get; set; }
[JsonPropertyName("max_ratio_act")]
public int? MaxRatioAct { get; set; }
[JsonPropertyName("max_ratio_enabled")]
public bool? MaxRatioEnabled { get; set; }
[JsonPropertyName("max_seeding_time")]
public int? MaxSeedingTime { get; set; }
[JsonPropertyName("max_seeding_time_enabled")]
public bool? MaxSeedingTimeEnabled { get; set; }
[JsonPropertyName("max_uploads")]
public int? MaxUploads { get; set; }
[JsonPropertyName("max_uploads_per_torrent")]
public int? MaxUploadsPerTorrent { get; set; }
[JsonPropertyName("memory_working_set_limit")]
public int? MemoryWorkingSetLimit { get; set; }
[JsonPropertyName("merge_trackers")]
public bool? MergeTrackers { get; set; }
[JsonPropertyName("outgoing_ports_max")]
public int? OutgoingPortsMax { get; set; }
[JsonPropertyName("outgoing_ports_min")]
public int? OutgoingPortsMin { get; set; }
[JsonPropertyName("peer_tos")]
public int? PeerTos { get; set; }
[JsonPropertyName("peer_turnover")]
public int? PeerTurnover { get; set; }
[JsonPropertyName("peer_turnover_cutoff")]
public int? PeerTurnoverCutoff { get; set; }
[JsonPropertyName("peer_turnover_interval")]
public int? PeerTurnoverInterval { get; set; }
[JsonPropertyName("performance_warning")]
public bool? PerformanceWarning { get; set; }
[JsonPropertyName("pex")]
public bool? Pex { get; set; }
[JsonPropertyName("preallocate_all")]
public bool? PreallocateAll { get; set; }
[JsonPropertyName("proxy_auth_enabled")]
public bool? ProxyAuthEnabled { get; set; }
[JsonPropertyName("proxy_bittorrent")]
public bool? ProxyBittorrent { get; set; }
[JsonPropertyName("proxy_hostname_lookup")]
public bool? ProxyHostnameLookup { get; set; }
[JsonPropertyName("proxy_ip")]
public string? ProxyIp { get; set; }
[JsonPropertyName("proxy_misc")]
public bool? ProxyMisc { get; set; }
[JsonPropertyName("proxy_password")]
public string? ProxyPassword { get; set; }
[JsonPropertyName("proxy_peer_connections")]
public bool? ProxyPeerConnections { get; set; }
[JsonPropertyName("proxy_port")]
public int? ProxyPort { get; set; }
[JsonPropertyName("proxy_rss")]
public bool? ProxyRss { get; set; }
[JsonPropertyName("proxy_type")]
public string? ProxyType { get; set; }
[JsonPropertyName("proxy_username")]
public string? ProxyUsername { get; set; }
[JsonPropertyName("queueing_enabled")]
public bool? QueueingEnabled { get; set; }
[JsonPropertyName("random_port")]
public bool? RandomPort { get; set; }
[JsonPropertyName("reannounce_when_address_changed")]
public bool? ReannounceWhenAddressChanged { get; set; }
[JsonPropertyName("recheck_completed_torrents")]
public bool? RecheckCompletedTorrents { get; set; }
[JsonPropertyName("refresh_interval")]
public int? RefreshInterval { get; set; }
[JsonPropertyName("request_queue_size")]
public int? RequestQueueSize { get; set; }
[JsonPropertyName("resolve_peer_countries")]
public bool? ResolvePeerCountries { get; set; }
[JsonPropertyName("resume_data_storage_type")]
public string? ResumeDataStorageType { get; set; }
[JsonPropertyName("rss_auto_downloading_enabled")]
public bool? RssAutoDownloadingEnabled { get; set; }
[JsonPropertyName("rss_download_repack_proper_episodes")]
public bool? RssDownloadRepackProperEpisodes { get; set; }
[JsonPropertyName("rss_max_articles_per_feed")]
public int? RssMaxArticlesPerFeed { get; set; }
[JsonPropertyName("rss_processing_enabled")]
public bool? RssProcessingEnabled { get; set; }
[JsonPropertyName("rss_refresh_interval")]
public int? RssRefreshInterval { get; set; }
[JsonPropertyName("rss_smart_episode_filters")]
public string? RssSmartEpisodeFilters { get; set; }
[JsonPropertyName("save_path")]
public string? SavePath { get; set; }
[JsonPropertyName("save_path_changed_tmm_enabled")]
public bool? SavePathChangedTmmEnabled { get; set; }
[JsonPropertyName("save_resume_data_interval")]
public int? SaveResumeDataInterval { get; set; }
[JsonPropertyName("scan_dirs")]
public Dictionary<string, SaveLocation>? ScanDirs { get; set; }
[JsonPropertyName("schedule_from_hour")]
public int? ScheduleFromHour { get; set; }
[JsonPropertyName("schedule_from_min")]
public int? ScheduleFromMin { get; set; }
[JsonPropertyName("schedule_to_hour")]
public int? ScheduleToHour { get; set; }
[JsonPropertyName("schedule_to_min")]
public int? ScheduleToMin { get; set; }
[JsonPropertyName("scheduler_days")]
public int? SchedulerDays { get; set; }
[JsonPropertyName("scheduler_enabled")]
public bool? SchedulerEnabled { get; set; }
[JsonPropertyName("send_buffer_low_watermark")]
public int? SendBufferLowWatermark { get; set; }
[JsonPropertyName("send_buffer_watermark")]
public int? SendBufferWatermark { get; set; }
[JsonPropertyName("send_buffer_watermark_factor")]
public int? SendBufferWatermarkFactor { get; set; }
[JsonPropertyName("slow_torrent_dl_rate_threshold")]
public int? SlowTorrentDlRateThreshold { get; set; }
[JsonPropertyName("slow_torrent_inactive_timer")]
public int? SlowTorrentInactiveTimer { get; set; }
[JsonPropertyName("slow_torrent_ul_rate_threshold")]
public int? SlowTorrentUlRateThreshold { get; set; }
[JsonPropertyName("socket_backlog_size")]
public int? SocketBacklogSize { get; set; }
[JsonPropertyName("socket_receive_buffer_size")]
public int? SocketReceiveBufferSize { get; set; }
[JsonPropertyName("socket_send_buffer_size")]
public int? SocketSendBufferSize { get; set; }
[JsonPropertyName("ssrf_mitigation")]
public bool? SsrfMitigation { get; set; }
[JsonPropertyName("start_paused_enabled")]
public bool? StartPausedEnabled { get; set; }
[JsonPropertyName("stop_tracker_timeout")]
public int? StopTrackerTimeout { get; set; }
[JsonPropertyName("temp_path")]
public string? TempPath { get; set; }
[JsonPropertyName("temp_path_enabled")]
public bool? TempPathEnabled { get; set; }
[JsonPropertyName("torrent_changed_tmm_enabled")]
public bool? TorrentChangedTmmEnabled { get; set; }
[JsonPropertyName("torrent_content_layout")]
public string? TorrentContentLayout { get; set; }
[JsonPropertyName("torrent_file_size_limit")]
public int? TorrentFileSizeLimit { get; set; }
[JsonPropertyName("torrent_stop_condition")]
public string? TorrentStopCondition { get; set; }
[JsonPropertyName("up_limit")]
public int? UpLimit { get; set; }
[JsonPropertyName("upload_choking_algorithm")]
public int? UploadChokingAlgorithm { get; set; }
[JsonPropertyName("upload_slots_behavior")]
public int? UploadSlotsBehavior { get; set; }
[JsonPropertyName("upnp")]
public bool? Upnp { get; set; }
[JsonPropertyName("upnp_lease_duration")]
public int? UpnpLeaseDuration { get; set; }
[JsonPropertyName("use_category_paths_in_manual_mode")]
public bool? UseCategoryPathsInManualMode { get; set; }
[JsonPropertyName("use_https")]
public bool? UseHttps { get; set; }
[JsonPropertyName("use_subcategories")]
public bool? UseSubcategories { get; set; }
[JsonPropertyName("utp_tcp_mixed_mode")]
public int? UtpTcpMixedMode { get; set; }
[JsonPropertyName("validate_https_tracker_certificate")]
public bool? ValidateHttpsTrackerCertificate { get; set; }
[JsonPropertyName("web_ui_address")]
public string? WebUiAddress { get; set; }
[JsonPropertyName("web_ui_ban_duration")]
public int? WebUiBanDuration { get; set; }
[JsonPropertyName("web_ui_clickjacking_protection_enabled")]
public bool? WebUiClickjackingProtectionEnabled { get; set; }
[JsonPropertyName("web_ui_csrf_protection_enabled")]
public bool? WebUiCsrfProtectionEnabled { get; set; }
[JsonPropertyName("web_ui_custom_http_headers")]
public string? WebUiCustomHttpHeaders { get; set; }
[JsonPropertyName("web_ui_domain_list")]
public string? WebUiDomainList { get; set; }
[JsonPropertyName("web_ui_host_header_validation_enabled")]
public bool? WebUiHostHeaderValidationEnabled { get; set; }
[JsonPropertyName("web_ui_https_cert_path")]
public string? WebUiHttpsCertPath { get; set; }
[JsonPropertyName("web_ui_https_key_path")]
public string? WebUiHttpsKeyPath { get; set; }
[JsonPropertyName("web_ui_max_auth_fail_count")]
public int? WebUiMaxAuthFailCount { get; set; }
[JsonPropertyName("web_ui_port")]
public int? WebUiPort { get; set; }
[JsonPropertyName("web_ui_reverse_proxies_list")]
public string? WebUiReverseProxiesList { get; set; }
[JsonPropertyName("web_ui_reverse_proxy_enabled")]
public bool? WebUiReverseProxyEnabled { get; set; }
[JsonPropertyName("web_ui_secure_cookie_enabled")]
public bool? WebUiSecureCookieEnabled { get; set; }
[JsonPropertyName("web_ui_session_timeout")]
public int? WebUiSessionTimeout { get; set; }
[JsonPropertyName("web_ui_upnp")]
public bool? WebUiUpnp { get; set; }
[JsonPropertyName("web_ui_use_custom_http_headers_enabled")]
public bool? WebUiUseCustomHttpHeadersEnabled { get; set; }
[JsonPropertyName("web_ui_username")]
public string? WebUiUsername { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient.Models
{
public record WebSeed
{
[JsonConstructor]
public WebSeed(string url)
{
Url = url;
}
[JsonPropertyName("url")]
public string Url { get; }
}
}

View File

@@ -0,0 +1,35 @@
namespace Lantean.QBitTorrentClient
{
public static class MultipartFormDataContentExtensions
{
public static void AddString(this MultipartFormDataContent content, string name, string value)
{
content.Add(new StringContent(value), name);
}
public static void AddString(this MultipartFormDataContent content, string name, bool value)
{
content.AddString(name, value ? "true" : "false");
}
public static void AddString(this MultipartFormDataContent content, string name, int value)
{
content.AddString(name, value.ToString());
}
public static void AddString(this MultipartFormDataContent content, string name, long value)
{
content.AddString(name, value.ToString());
}
public static void AddString(this MultipartFormDataContent content, string name, float value)
{
content.AddString(name, value.ToString());
}
public static void AddString(this MultipartFormDataContent content, string name, DateTimeOffset value, bool useSeconds = true)
{
content.AddString(name, useSeconds ? value.ToUnixTimeSeconds() : value.ToUnixTimeMilliseconds());
}
}
}

View File

@@ -0,0 +1,67 @@
using System.Text;
namespace Lantean.QBitTorrentClient
{
public class QueryBuilder
{
private readonly IList<KeyValuePair<string, string>> _parameters;
public QueryBuilder()
{
_parameters = [];
}
public QueryBuilder(IList<KeyValuePair<string, string>> parameters)
{
_parameters = parameters;
}
public QueryBuilder Add(string key, string value)
{
_parameters.Add(new KeyValuePair<string, string>(key, value));
return this;
}
public QueryBuilder AddIfNotNullOrEmpty(string key, string value)
{
if (!string.IsNullOrEmpty(value))
{
_parameters.Add(new KeyValuePair<string, string>(key, value));
}
return this;
}
public string ToQueryString()
{
if (_parameters.Count == 0)
{
return string.Empty;
}
var queryString = new StringBuilder();
for (int i = 0; i < _parameters.Count; i++)
{
var kvp = _parameters[i];
if (i == 0)
{
queryString.Append('?');
}
else
{
queryString.Append('&');
}
queryString.Append(Uri.EscapeDataString(kvp.Key));
queryString.Append('=');
queryString.Append(Uri.EscapeDataString(kvp.Value));
}
return queryString.ToString();
}
public override string ToString()
{
return ToQueryString();
}
}
}

View File

@@ -0,0 +1,40 @@
namespace Lantean.QBitTorrentClient
{
public static class QueryBuilderExtensions
{
public static QueryBuilder Add(this QueryBuilder builder, string key, bool value)
{
return builder.Add(key, value ? "true" : "false");
}
public static QueryBuilder Add(this QueryBuilder builder, string key, int value)
{
return builder.Add(key, value.ToString());
}
public static QueryBuilder Add(this QueryBuilder builder, string key, long value)
{
return builder.Add(key, value.ToString());
}
public static QueryBuilder Add(this QueryBuilder builder, string key, DateTimeOffset value, bool useSeconds = true)
{
return builder.Add(key, useSeconds ? value.ToUnixTimeSeconds() : value.ToUnixTimeMilliseconds());
}
public static QueryBuilder Add(this QueryBuilder builder, string key, Enum value)
{
return builder.Add(key, value.ToString());
}
public static QueryBuilder AddPipeSeparated<T>(this QueryBuilder builder, string key, IEnumerable<T> values)
{
return builder.Add(key, string.Join('|', values));
}
public static QueryBuilder AddCommaSeparated<T>(this QueryBuilder builder, string key, IEnumerable<T> values)
{
return builder.Add(key, string.Join(',', values));
}
}
}

View File

@@ -0,0 +1,19 @@
using Lantean.QBitTorrentClient.Converters;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Lantean.QBitTorrentClient
{
public static class SerializerOptions
{
public static JsonSerializerOptions Options { get; }
static SerializerOptions()
{
Options = new JsonSerializerOptions();
Options.Converters.Add(new StringFloatJsonConverter());
Options.Converters.Add(new SaveLocationJsonConverter());
Options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
}
}
}