Compare commits
5 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
858bd7c95c | ||
|
|
2822653d3b | ||
|
|
e8a86cf806 | ||
|
|
38530cd6fd | ||
|
|
a7fb1e3e9d |
7 changed files with 164 additions and 23 deletions
|
|
@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased
|
## [Unreleased
|
||||||
|
|
||||||
|
## [2.0.138] - 2026-06-05
|
||||||
|
### Fixed
|
||||||
|
- SABnzbd fixes.
|
||||||
|
|
||||||
|
## [2.0.137] - 2026-06-05
|
||||||
|
### Fixed
|
||||||
|
- Fix torbox save path mapping for single file downloads.
|
||||||
|
|
||||||
## [2.0.136] - 2026-05-30
|
## [2.0.136] - 2026-05-30
|
||||||
### Added
|
### Added
|
||||||
- Added devcontainer development workflow.
|
- Added devcontainer development workflow.
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,64 @@ public class SabnzbdTest
|
||||||
Assert.Equal("Failed", result.Slots[0].Status);
|
Assert.Equal("Failed", result.Slots[0].Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(TorrentFinishedAction.RemoveAllTorrents, true, true, true, true)]
|
||||||
|
[InlineData(TorrentFinishedAction.RemoveAllTorrents, false, true, true, false)]
|
||||||
|
[InlineData(TorrentFinishedAction.RemoveRealDebrid, true, false, true, true)]
|
||||||
|
[InlineData(TorrentFinishedAction.RemoveRealDebrid, false, false, true, false)]
|
||||||
|
[InlineData(TorrentFinishedAction.RemoveClient, true, true, false, true)]
|
||||||
|
[InlineData(TorrentFinishedAction.RemoveClient, false, true, false, false)]
|
||||||
|
public async Task Delete_ShouldRespectFinishedActionAndDeleteFiles(
|
||||||
|
TorrentFinishedAction finishedAction,
|
||||||
|
Boolean deleteFiles,
|
||||||
|
Boolean expectedDeleteData,
|
||||||
|
Boolean expectedDeleteRdTorrent,
|
||||||
|
Boolean expectedDeleteLocalFiles)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var torrentId = Guid.NewGuid();
|
||||||
|
_settings.Current.Integrations.Default.FinishedAction = finishedAction;
|
||||||
|
|
||||||
|
_torrentsMock.Setup(t => t.GetByHash("hash1"))
|
||||||
|
.ReturnsAsync(new Torrent
|
||||||
|
{
|
||||||
|
TorrentId = torrentId,
|
||||||
|
Hash = "hash1",
|
||||||
|
Type = DownloadType.Nzb
|
||||||
|
});
|
||||||
|
|
||||||
|
var sabnzbd = new Sabnzbd(_loggerMock.Object, _torrentsMock.Object, _appSettings, _settings);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await sabnzbd.Delete("hash1", deleteFiles);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_torrentsMock.Verify(t => t.Delete(torrentId, expectedDeleteData, expectedDeleteRdTorrent, expectedDeleteLocalFiles), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Delete_ShouldNotDelete_WhenFinishedActionIsNone()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
_settings.Current.Integrations.Default.FinishedAction = TorrentFinishedAction.None;
|
||||||
|
|
||||||
|
_torrentsMock.Setup(t => t.GetByHash("hash1"))
|
||||||
|
.ReturnsAsync(new Torrent
|
||||||
|
{
|
||||||
|
TorrentId = Guid.NewGuid(),
|
||||||
|
Hash = "hash1",
|
||||||
|
Type = DownloadType.Nzb
|
||||||
|
});
|
||||||
|
|
||||||
|
var sabnzbd = new Sabnzbd(_loggerMock.Object, _torrentsMock.Object, _appSettings, _settings);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await sabnzbd.Delete("hash1", true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_torrentsMock.Verify(t => t.Delete(It.IsAny<Guid>(), It.IsAny<Boolean>(), It.IsAny<Boolean>(), It.IsAny<Boolean>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetConfig_ShouldReturnCorrectConfig()
|
public void GetConfig_ShouldReturnCorrectConfig()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -359,6 +359,11 @@ public class QBittorrent(ILogger<QBittorrent> logger, ISettings settings, Authen
|
||||||
var selectedFileName = topLevelSelectedFiles[0]!;
|
var selectedFileName = topLevelSelectedFiles[0]!;
|
||||||
var selectedFileBaseName = Path.GetFileNameWithoutExtension(selectedFileName);
|
var selectedFileBaseName = Path.GetFileNameWithoutExtension(selectedFileName);
|
||||||
|
|
||||||
|
if (torrent.ClientKind == Provider.TorBox)
|
||||||
|
{
|
||||||
|
return selectedFileBaseName;
|
||||||
|
}
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(selectedFileBaseName) &&
|
if (!String.IsNullOrWhiteSpace(selectedFileBaseName) &&
|
||||||
selectedFileBaseName.Equals(torrent.RdName, StringComparison.OrdinalIgnoreCase))
|
selectedFileBaseName.Equals(torrent.RdName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
||||||
return result.Hash;
|
return result.Hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task Delete(String hash)
|
public virtual async Task Delete(String hash, Boolean deleteFiles = false)
|
||||||
{
|
{
|
||||||
var torrent = await torrents.GetByHash(hash);
|
var torrent = await torrents.GetByHash(hash);
|
||||||
|
|
||||||
|
|
@ -189,18 +189,18 @@ public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings app
|
||||||
switch (settings.Current.Integrations.Default.FinishedAction)
|
switch (settings.Current.Integrations.Default.FinishedAction)
|
||||||
{
|
{
|
||||||
case TorrentFinishedAction.RemoveAllTorrents:
|
case TorrentFinishedAction.RemoveAllTorrents:
|
||||||
logger.LogDebug("Removing nzb from debrid provider and RDT-Client, no files");
|
logger.LogDebug("Removing nzb from debrid provider and RDT-Client, {Files}", deleteFiles ? "with files" : "no files");
|
||||||
await torrents.Delete(torrent.TorrentId, true, true, true);
|
await torrents.Delete(torrent.TorrentId, true, true, deleteFiles);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TorrentFinishedAction.RemoveRealDebrid:
|
case TorrentFinishedAction.RemoveRealDebrid:
|
||||||
logger.LogDebug("Removing nzb from debrid provider, no files");
|
logger.LogDebug("Removing nzb from debrid provider, {Files}", deleteFiles ? "with files" : "no files");
|
||||||
await torrents.Delete(torrent.TorrentId, false, true, true);
|
await torrents.Delete(torrent.TorrentId, false, true, deleteFiles);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TorrentFinishedAction.RemoveClient:
|
case TorrentFinishedAction.RemoveClient:
|
||||||
logger.LogDebug("Removing nzb from client, no files");
|
logger.LogDebug("Removing nzb from client, {Files}", deleteFiles ? "with files" : "no files");
|
||||||
await torrents.Delete(torrent.TorrentId, true, false, true);
|
await torrents.Delete(torrent.TorrentId, true, false, deleteFiles);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TorrentFinishedAction.None:
|
case TorrentFinishedAction.None:
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ public class Settings(IServiceScopeFactory serviceScopeFactory) : ISettings
|
||||||
{
|
{
|
||||||
var downloadPath = settings.DownloadClient.MappedPath;
|
var downloadPath = settings.DownloadClient.MappedPath;
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(downloadPath))
|
||||||
|
{
|
||||||
|
downloadPath = settings.DownloadClient.DownloadPath;
|
||||||
|
}
|
||||||
|
|
||||||
downloadPath = downloadPath.TrimEnd('\\')
|
downloadPath = downloadPath.TrimEnd('\\')
|
||||||
.TrimEnd('/');
|
.TrimEnd('/');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,30 @@ public class SabnzbdControllerTest
|
||||||
Assert.IsType<OkObjectResult>(result);
|
Assert.IsType<OkObjectResult>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("?name=delete&value=hash1&del_files=1", true)]
|
||||||
|
[InlineData("?name=delete&value=hash1&del_files=true", true)]
|
||||||
|
[InlineData("?name=delete&value=hash1&del_files=0", false)]
|
||||||
|
[InlineData("?name=delete&value=hash1", false)]
|
||||||
|
public async Task Queue_Delete_PassesDeleteFilesFlag(String queryString, Boolean expectedDeleteFiles)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.Request.QueryString = new(queryString);
|
||||||
|
_controller.ControllerContext.HttpContext = httpContext;
|
||||||
|
|
||||||
|
_sabnzbdMock.Setup(s => s.Delete("hash1", expectedDeleteFiles)).Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _controller.Queue();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
|
var response = Assert.IsType<SabnzbdResponse>(okResult.Value);
|
||||||
|
Assert.True(response.Status);
|
||||||
|
_sabnzbdMock.Verify(s => s.Delete("hash1", expectedDeleteFiles), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetHistory_ReturnsOk()
|
public async Task GetHistory_ReturnsOk()
|
||||||
{
|
{
|
||||||
|
|
@ -129,6 +153,26 @@ public class SabnzbdControllerTest
|
||||||
Assert.Equal(1, response.History.NoOfSlots);
|
Assert.Equal(1, response.History.NoOfSlots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task History_Delete_PassesDeleteFilesFlag()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.Request.QueryString = new("?name=delete&value=hash1&del_files=1");
|
||||||
|
_controller.ControllerContext.HttpContext = httpContext;
|
||||||
|
|
||||||
|
_sabnzbdMock.Setup(s => s.Delete("hash1", true)).Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _controller.History();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
|
var response = Assert.IsType<SabnzbdResponse>(okResult.Value);
|
||||||
|
Assert.True(response.Status);
|
||||||
|
_sabnzbdMock.Verify(s => s.Delete("hash1", true), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetVersion_HasAllowAnonymousAttribute()
|
public void GetVersion_HasAllowAnonymousAttribute()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -56,22 +56,7 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
||||||
|
|
||||||
if (name == "delete")
|
if (name == "delete")
|
||||||
{
|
{
|
||||||
var value = GetParam("value");
|
return await DeleteDownloads();
|
||||||
|
|
||||||
if (String.IsNullOrWhiteSpace(value))
|
|
||||||
{
|
|
||||||
return BadRequest(new SabnzbdResponse
|
|
||||||
{
|
|
||||||
Error = "No value specified for delete operation"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await sabnzbd.Delete(value ?? "");
|
|
||||||
|
|
||||||
return Ok(new SabnzbdResponse
|
|
||||||
{
|
|
||||||
Status = true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(new SabnzbdResponse
|
return Ok(new SabnzbdResponse
|
||||||
|
|
@ -85,6 +70,12 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
||||||
public async Task<ActionResult> History()
|
public async Task<ActionResult> History()
|
||||||
{
|
{
|
||||||
logger.LogDebug("Sabnzbd mode: history");
|
logger.LogDebug("Sabnzbd mode: history");
|
||||||
|
var name = GetParam("name");
|
||||||
|
|
||||||
|
if (name == "delete")
|
||||||
|
{
|
||||||
|
return await DeleteDownloads();
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(new SabnzbdResponse
|
return Ok(new SabnzbdResponse
|
||||||
{
|
{
|
||||||
|
|
@ -183,6 +174,31 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<ActionResult> DeleteDownloads()
|
||||||
|
{
|
||||||
|
var value = GetParam("value");
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return BadRequest(new SabnzbdResponse
|
||||||
|
{
|
||||||
|
Error = "No value specified for delete operation"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var deleteFiles = IsTrue(GetParam("del_files"));
|
||||||
|
|
||||||
|
foreach (var hash in value.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||||
|
{
|
||||||
|
await sabnzbd.Delete(hash, deleteFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(new SabnzbdResponse
|
||||||
|
{
|
||||||
|
Status = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private String? GetParam(String name)
|
private String? GetParam(String name)
|
||||||
{
|
{
|
||||||
var value = Request.Query[name].ToString();
|
var value = Request.Query[name].ToString();
|
||||||
|
|
@ -194,4 +210,9 @@ public class SabnzbdController(ILogger<SabnzbdController> logger, Sabnzbd sabnzb
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Boolean IsTrue(String? value)
|
||||||
|
{
|
||||||
|
return value is "1" || Boolean.TryParse(value, out var result) && result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue