using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.QBittorrent;
using RdtClient.Service.Services;
using RealDebridException = RDNET.RealDebridException;
namespace RdtClient.Web.Controllers;
///
/// This API behaves as a regular QBittorrent 4+ API
/// Documentation is found here: https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)
///
[ApiController]
[Route("api/v2")]
[Route("qbittorrent/api/v2")]
public class QBittorrentController(ILogger logger, QBittorrent qBittorrent) : Controller
{
[AllowAnonymous]
[Route("auth/login")]
[HttpGet]
public async Task AuthLogin([FromQuery] QBAuthLoginRequest request)
{
logger.LogDebug($"Auth login");
if (Settings.Get.General.AuthenticationType == AuthenticationType.None)
{
return Ok("Ok.");
}
if (String.IsNullOrWhiteSpace(request.UserName) || String.IsNullOrEmpty(request.Password))
{
return Ok("Fails.");
}
var result = await qBittorrent.AuthLogin(request.UserName, request.Password);
if (result)
{
return Ok("Ok.");
}
return Ok("Fails.");
}
[AllowAnonymous]
[Route("auth/login")]
[HttpPost]
public async Task AuthLoginPost([FromForm] QBAuthLoginRequest request)
{
return await AuthLogin(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("auth/logout")]
[HttpGet]
[HttpPost]
public async Task AuthLogout()
{
logger.LogDebug($"Auth logout");
await qBittorrent.AuthLogout();
return Ok();
}
[AllowAnonymous]
[Route("app/version")]
[HttpGet]
[HttpPost]
public ActionResult AppVersion()
{
return Ok("v4.3.2");
}
[AllowAnonymous]
[Route("app/webapiVersion")]
[HttpGet]
[HttpPost]
public ActionResult AppWebVersion()
{
return Ok("2.7");
}
[AllowAnonymous]
[Route("app/buildInfo")]
[HttpGet]
[HttpPost]
public ActionResult AppBuildInfo()
{
var result = new AppBuildInfo
{
Bitness = 64,
Boost = "1.75.0",
Libtorrent = "1.2.11.0",
Openssl = "1.1.1i",
Qt = "5.15.2",
Zlib = "1.2.11"
};
return Ok(result);
}
[Authorize(Policy = "AuthSetting")]
[Route("app/shutdown")]
[HttpGet]
[HttpPost]
public ActionResult AppShutdown()
{
return Ok();
}
[AllowAnonymous]
[Route("app/preferences")]
[HttpGet]
[HttpPost]
public async Task> AppPreferences()
{
var result = await qBittorrent.AppPreferences();
return Ok(result);
}
[Authorize(Policy = "AuthSetting")]
[Route("app/setPreferences")]
[HttpGet]
[HttpPost]
public ActionResult AppSetPreferences()
{
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("app/defaultSavePath")]
[HttpGet]
[HttpPost]
public ActionResult AppDefaultSavePath()
{
var result = Settings.AppDefaultSavePath;
return Ok(result);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/info")]
[HttpGet]
[HttpPost]
public async Task>> TorrentsInfo([FromQuery] QBTorrentsInfoRequest request)
{
var results = await qBittorrent.TorrentInfo();
if (!String.IsNullOrWhiteSpace(request.Category))
{
results = results.Where(m => m.Category == request.Category).ToList();
}
return Ok(results);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/info")]
[HttpPost]
public async Task>> TorrentsFilesPost([FromForm] QBTorrentsInfoRequest request)
{
return await TorrentsInfo(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/files")]
[HttpGet]
public async Task>> TorrentsFiles([FromQuery] QBTorrentsHashRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hash))
{
return BadRequest();
}
var result = await qBittorrent.TorrentFileContents(request.Hash);
if (result == null)
{
return NotFound();
}
return Ok(result);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/files")]
[HttpPost]
public async Task>> TorrentsFilesPost([FromForm] QBTorrentsHashRequest request)
{
return await TorrentsFiles(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/properties")]
[HttpGet]
public async Task>> TorrentsProperties([FromQuery] QBTorrentsHashRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hash))
{
return BadRequest();
}
var result = await qBittorrent.TorrentProperties(request.Hash);
if (result == null)
{
return NotFound();
}
return Ok(result);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/properties")]
[HttpPost]
public async Task>> TorrentsPropertiesPost([FromForm] QBTorrentsHashRequest request)
{
return await TorrentsProperties(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/pause")]
[HttpGet]
public async Task TorrentsPause([FromQuery] QBTorrentsHashesRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await qBittorrent.TorrentPause(hash);
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/pause")]
[HttpPost]
public async Task TorrentsPausePost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsPause(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/resume")]
[HttpGet]
public async Task TorrentsResume([FromQuery] QBTorrentsHashesRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await qBittorrent.TorrentResume(hash);
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/resume")]
[HttpPost]
public async Task TorrentsResumePost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsResume(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/setShareLimits")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsSetShareLimits()
{
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/delete")]
[HttpGet]
public async Task TorrentsDelete([FromQuery] QBTorrentsDeleteRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
logger.LogDebug("Delete {Hashes}", request.Hashes);
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await qBittorrent.TorrentsDelete(hash, request.DeleteFiles);
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/delete")]
[HttpPost]
public async Task TorrentsDeletePost([FromForm] QBTorrentsDeleteRequest request)
{
return await TorrentsDelete(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/add")]
[HttpGet]
public async Task TorrentsAdd([FromQuery] QBTorrentsAddRequest request)
{
if (String.IsNullOrWhiteSpace(request.Urls))
{
return BadRequest();
}
var urls = request.Urls.Split("\n");
foreach (var url in urls)
{
try
{
if (url.StartsWith("magnet"))
{
await qBittorrent.TorrentsAddMagnet(url.Trim(), request.Category, null);
}
else if (url.StartsWith("http"))
{
var httpClient = new HttpClient();
var result = await httpClient.GetByteArrayAsync(url);
await qBittorrent.TorrentsAddFile(result, request.Category, null);
}
else
{
return BadRequest($"Invalid torrent link format {url}");
}
}
catch (RealDebridException ex)
{
// Infringing file.
if (ex.ErrorCode == 35)
{
return Ok("Fails.");
}
}
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/add")]
[HttpPost]
public async Task TorrentsAddPost([FromForm] QBTorrentsAddRequest request)
{
foreach (var file in Request.Form.Files)
{
if (file.Length > 0)
{
await using var target = new MemoryStream();
await file.CopyToAsync(target);
var fileBytes = target.ToArray();
await qBittorrent.TorrentsAddFile(fileBytes, request.Category, request.Priority);
}
}
if (request.Urls != null)
{
return await TorrentsAdd(request);
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/filePrio")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsFilePrio()
{
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/setCategory")]
[HttpGet]
public async Task TorrentsSetCategory([FromQuery] QBTorrentsSetCategoryRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await qBittorrent.TorrentsSetCategory(hash, request.Category);
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/setCategory")]
[HttpPost]
public async Task TorrentsSetCategoryPost([FromForm] QBTorrentsSetCategoryRequest request)
{
return await TorrentsSetCategory(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/categories")]
[HttpGet]
[HttpPost]
public async Task>> TorrentsCategories()
{
var categories = await qBittorrent.TorrentsCategories();
return Ok(categories);
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/createCategory")]
[HttpGet]
[HttpPost]
public async Task TorrentsCreateCategory([FromForm] QBTorrentsCreateCategoryRequest request)
{
if (String.IsNullOrWhiteSpace(request.Category))
{
return BadRequest("category name is empty");
}
await qBittorrent.CategoryCreate(request.Category.Trim());
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/createTags")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsCreateTags()
{
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/removeCategories")]
[HttpGet]
[HttpPost]
public async Task TorrentsRemoveCategories([FromForm] QBTorrentsRemoveCategoryRequest request)
{
if (String.IsNullOrWhiteSpace(request.Categories))
{
return Ok();
}
var categories = request.Categories.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var category in categories)
{
await qBittorrent.CategoryRemove(category.Trim());
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/setForcestart")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsSetForceStart()
{
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/tags")]
[HttpGet]
[HttpPost]
public ActionResult TorrentsTags()
{
return Ok(new List());
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/topPrio")]
[HttpGet]
public async Task TorrentsTopPrio([FromQuery] QBTorrentsHashesRequest request)
{
if (String.IsNullOrWhiteSpace(request.Hashes))
{
return BadRequest();
}
var hashes = request.Hashes.Split("|");
foreach (var hash in hashes)
{
await qBittorrent.TorrentsTopPrio(hash);
}
return Ok();
}
[Authorize(Policy = "AuthSetting")]
[Route("torrents/topPrio")]
[HttpPost]
public async Task TorrentsTopPrioPost([FromForm] QBTorrentsHashesRequest request)
{
return await TorrentsTopPrio(request);
}
[Authorize(Policy = "AuthSetting")]
[Route("sync/maindata")]
[HttpGet]
public async Task SyncMainData()
{
var result = await qBittorrent.SyncMainData();
return Ok(result);
}
[Authorize(Policy = "AuthSetting")]
[Route("sync/maindata")]
[HttpPost]
public async Task SyncMainDataPost()
{
return await SyncMainData();
}
[Authorize(Policy = "AuthSetting")]
[Route("transfer/info")]
[HttpGet]
public ActionResult TransferInfo()
{
return Ok(QBittorrent.TransferInfo());
}
[Authorize(Policy = "AuthSetting")]
[Route("transfer/info")]
[HttpPost]
public ActionResult TransferInfoPost()
{
return TransferInfo();
}
}
public class QBAuthLoginRequest
{
public String? UserName { get; set; }
public String? Password { get; set; }
}
public class QBTorrentsInfoRequest
{
public String? Category { get; set; }
}
public class QBTorrentsHashRequest
{
public String? Hash { get; set; }
}
public class QBTorrentsDeleteRequest
{
public String? Hashes { get; set; }
public Boolean DeleteFiles { get; set; }
}
public class QBTorrentsAddRequest
{
public String? Urls { get; set; }
public String? Category { get; set; }
public Int32? Priority { get; set; }
}
public class QBTorrentsSetCategoryRequest
{
public String? Hashes { get; set; }
public String? Category { get; set; }
}
public class QBTorrentsCreateCategoryRequest
{
public String? Category { get; set; }
}
public class QBTorrentsRemoveCategoryRequest
{
public String? Categories { get; set; }
}
public class QBTorrentsHashesRequest
{
public String? Hashes { get; set; }
}