rdt-client/server/RdtClient.Service/Services/Sabnzbd.cs
2026-05-18 21:30:31 +02:00

259 lines
12 KiB
C#

using Microsoft.Extensions.Logging;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.Internal;
using RdtClient.Data.Models.Sabnzbd;
using RdtClient.Service.Helpers;
namespace RdtClient.Service.Services;
public class Sabnzbd(ILogger<Sabnzbd> logger, Torrents torrents, AppSettings appSettings, ISettings settings)
{
public virtual async Task<SabnzbdQueue> GetQueue()
{
var allTorrents = await torrents.Get();
var activeTorrents = allTorrents.Where(t => t.Type == DownloadType.Nzb && t.Completed == null).ToList();
var queue = new SabnzbdQueue
{
NoOfSlots = activeTorrents.Count,
Slots = activeTorrents.Select((t, index) =>
{
var rdProgress = Math.Clamp(t.RdProgress ?? 0.0, 0.0, 100.0) / 100.0;
Double progress;
var dlStats = t.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
if (dlStats.Count > 0)
{
var bytesDone = dlStats.Sum(m => m.BytesDone);
var bytesTotal = dlStats.Sum(m => m.BytesTotal);
var downloadProgress = bytesTotal > 0 ? Math.Clamp((Double)bytesDone / bytesTotal, 0.0, 1.0) : 0;
progress = (rdProgress + downloadProgress) / 2.0;
}
else
{
progress = rdProgress;
}
var timeLeft = "0:00:00";
var startTime = t.Retry > t.Added ? t.Retry.Value : t.Added;
var elapsed = DateTimeOffset.UtcNow - startTime;
if (progress is > 0 and < 1.0)
{
var totalEstimatedTime = TimeSpan.FromTicks((Int64)(elapsed.Ticks / progress));
var remaining = totalEstimatedTime - elapsed;
if (remaining.TotalSeconds > 0)
{
timeLeft = $"{(Int32)remaining.TotalHours}:{remaining.Minutes:D2}:{remaining.Seconds:D2}";
}
}
return new SabnzbdQueueSlot
{
Index = index,
NzoId = t.Hash,
Filename = t.RdName ?? t.Hash,
Size = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal)),
SizeLeft = FileSizeHelper.FormatSize(dlStats.Sum(d => d.BytesTotal - d.BytesDone)),
Percentage = (progress * 100.0).ToString("0"),
Status = t.RdStatus switch
{
TorrentStatus.Queued => "Queued",
TorrentStatus.Processing => "Downloading",
TorrentStatus.WaitingForFileSelection => "Downloading",
TorrentStatus.Downloading => "Downloading",
TorrentStatus.Uploading => "Downloading",
TorrentStatus.Finished => "Completed",
TorrentStatus.Error => "Failed",
_ => "Downloading"
},
Category = t.Category ?? "*",
Priority = "Normal",
TimeLeft = timeLeft
};
})
.ToList()
};
return queue;
}
public virtual async Task<SabnzbdHistory> GetHistory()
{
var allTorrents = await torrents.Get();
var completedTorrents = allTorrents.Where(t => t.Type == DownloadType.Nzb && t.Completed != null).ToList();
var savePath = settings.DefaultSavePath;
var history = new SabnzbdHistory
{
NoOfSlots = completedTorrents.Count,
TotalSlots = completedTorrents.Count,
Slots = completedTorrents.Select(t =>
{
var path = savePath;
if (!String.IsNullOrWhiteSpace(t.Category))
{
path = Path.Combine(path, t.Category);
}
if (!String.IsNullOrWhiteSpace(t.RdName))
{
path = Path.Combine(path, t.RdName);
}
return new SabnzbdHistorySlot
{
NzoId = t.Hash,
Name = t.RdName ?? t.Hash,
Size = FileSizeHelper.FormatSize(t.Downloads.Sum(d => d.BytesTotal)),
Status = String.IsNullOrWhiteSpace(t.Error) ? "Completed" : "Failed",
Category = t.Category ?? "Default",
Path = path
};
})
.ToList()
};
return history;
}
public virtual async Task<String> AddFile(Byte[] fileBytes, String? fileName, String? category, Int32? priority)
{
logger.LogDebug($"Add file {category}");
var torrent = new Torrent
{
Category = category,
DownloadClient = settings.Current.DownloadClient.Client,
HostDownloadAction = settings.Current.Integrations.Default.HostDownloadAction,
FinishedActionDelay = settings.Current.Integrations.Default.FinishedActionDelay,
DownloadAction = settings.Current.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
FinishedAction = TorrentFinishedAction.None,
DownloadMinSize = settings.Current.Integrations.Default.MinFileSize,
IncludeRegex = settings.Current.Integrations.Default.IncludeRegex,
ExcludeRegex = settings.Current.Integrations.Default.ExcludeRegex,
TorrentRetryAttempts = settings.Current.Integrations.Default.TorrentRetryAttempts,
DownloadRetryAttempts = settings.Current.Integrations.Default.DownloadRetryAttempts,
DeleteOnError = settings.Current.Integrations.Default.DeleteOnError,
Lifetime = settings.Current.Integrations.Default.TorrentLifetime,
Priority = (priority ?? settings.Current.Integrations.Default.Priority) > 0 ? 1 : null
};
var result = await torrents.AddNzbFileToDebridQueue(fileBytes, fileName, torrent);
return result.Hash;
}
public virtual async Task<String> AddUrl(String url, String? category, Int32? priority)
{
logger.LogDebug($"Add url {category}");
var torrent = new Torrent
{
Category = category,
DownloadClient = settings.Current.DownloadClient.Client,
HostDownloadAction = settings.Current.Integrations.Default.HostDownloadAction,
FinishedActionDelay = settings.Current.Integrations.Default.FinishedActionDelay,
DownloadAction = settings.Current.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
FinishedAction = TorrentFinishedAction.None,
DownloadMinSize = settings.Current.Integrations.Default.MinFileSize,
IncludeRegex = settings.Current.Integrations.Default.IncludeRegex,
ExcludeRegex = settings.Current.Integrations.Default.ExcludeRegex,
TorrentRetryAttempts = settings.Current.Integrations.Default.TorrentRetryAttempts,
DownloadRetryAttempts = settings.Current.Integrations.Default.DownloadRetryAttempts,
DeleteOnError = settings.Current.Integrations.Default.DeleteOnError,
Lifetime = settings.Current.Integrations.Default.TorrentLifetime,
Priority = priority ?? (settings.Current.Integrations.Default.Priority > 0 ? settings.Current.Integrations.Default.Priority : null)
};
var result = await torrents.AddNzbLinkToDebridQueue(url, torrent);
return result.Hash;
}
public virtual async Task Delete(String hash)
{
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Nzb)
{
return;
}
switch (settings.Current.Integrations.Default.FinishedAction)
{
case TorrentFinishedAction.RemoveAllTorrents:
logger.LogDebug("Removing nzb from debrid provider and RDT-Client, no files");
await torrents.Delete(torrent.TorrentId, true, true, true);
break;
case TorrentFinishedAction.RemoveRealDebrid:
logger.LogDebug("Removing nzb from debrid provider, no files");
await torrents.Delete(torrent.TorrentId, false, true, true);
break;
case TorrentFinishedAction.RemoveClient:
logger.LogDebug("Removing nzb from client, no files");
await torrents.Delete(torrent.TorrentId, true, false, true);
break;
case TorrentFinishedAction.None:
logger.LogDebug("Not removing nzb files");
break;
default:
logger.LogDebug($"Invalid nzb FinishedAction {torrent.FinishedAction}", torrent);
break;
}
}
public virtual List<String> GetCategories()
{
var categoryList = (settings.Current.General.Categories ?? "")
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(m => m.Trim())
.Where(m => m != "*")
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.ToList();
categoryList.Insert(0, "*");
return categoryList;
}
public virtual SabnzbdConfig GetConfig()
{
var savePath = settings.DefaultSavePath;
var categoryList = GetCategories();
var categories = categoryList.Select((c, i) => new SabnzbdCategory
{
Name = c,
Order = i,
Dir = c == "*" ? "" : Path.Combine(savePath, c)
})
.ToList();
var config = new SabnzbdConfig
{
Misc = new()
{
CompleteDir = savePath,
DownloadDir = savePath,
Port = appSettings.Port.ToString(),
Version = "4.4.0"
},
Categories = categories
};
return config;
}
}