rdt-client/server/RdtClient.Service/Services/QBittorrent.cs
Roger Far e8a86cf806
Some checks failed
Release Docker Image / build (map[arch:amd64 platform:linux/amd64 runs-on:ubuntu-latest]) (push) Has been cancelled
Release Docker Image / build (map[arch:arm64 platform:linux/arm64 runs-on:ubuntu-24.04-arm]) (push) Has been cancelled
Create GitHub Release / Test, Build, and Bundle (push) Has been cancelled
dotnet test / build (push) Has been cancelled
Release Docker Image / push-images (push) Has been cancelled
Create GitHub Release / Create GitHub release (push) Has been cancelled
Fix torbox save path mapping for single file downloads.
2026-06-05 20:16:14 -06:00

888 lines
32 KiB
C#

using Microsoft.Extensions.Logging;
using RdtClient.Data.Enums;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.QBittorrent;
namespace RdtClient.Service.Services;
public class QBittorrent(ILogger<QBittorrent> logger, ISettings settings, Authentication authentication, Torrents torrents, Downloads downloads, ITorrentRunnerState runnerState)
{
public async Task<Boolean> AuthLogin(String userName, String password)
{
logger.LogDebug("Auth login");
var login = await authentication.Login(userName, password);
return login.Succeeded;
}
public async Task AuthLogout()
{
logger.LogDebug("Auth logout");
await authentication.Logout();
}
public async Task<AppPreferences> AppPreferences()
{
var preferences = new AppPreferences
{
AddTrackers = "",
AddTrackersEnabled = false,
AltDlLimit = 10240,
AltUpLimit = 10240,
AlternativeWebuiEnabled = false,
AlternativeWebuiPath = "",
AnnounceIp = "",
AnnounceToAllTiers = true,
AnnounceToAllTrackers = false,
AnonymousMode = false,
AsyncIoThreads = 4,
AutoDeleteMode = 0,
AutoTmmEnabled = false,
AutorunEnabled = false,
AutorunProgram = "",
BannedIPs = "",
BittorrentProtocol = 0,
BypassAuthSubnetWhitelist = "",
BypassAuthSubnetWhitelistEnabled = false,
BypassLocalAuth = false,
CategoryChangedTmmEnabled = false,
CheckingMemoryUse = 32,
CreateSubfolderEnabled = true,
CurrentInterfaceAddress = "",
CurrentNetworkInterface = "",
Dht = true,
DiskCache = -1,
DiskCacheTtl = 60,
DlLimit = 0,
DontCountSlowTorrents = false,
DyndnsDomain = "changeme.dyndns.org",
DyndnsEnabled = false,
DyndnsPassword = "",
DyndnsService = 0,
DyndnsUsername = "",
EmbeddedTrackerPort = 9000,
EnableCoalesceReadWrite = true,
EnableEmbeddedTracker = false,
EnableMultiConnectionsFromSameIp = false,
EnableOsCache = true,
EnablePieceExtentAffinity = false,
EnableSuperSeeding = false,
EnableUploadSuggestions = false,
Encryption = 0,
ExportDir = "",
ExportDirFin = "",
FilePoolSize = 40,
IncompleteFilesExt = false,
IpFilterEnabled = false,
IpFilterPath = "",
IpFilterTrackers = false,
LimitLanPeers = true,
LimitTcpOverhead = false,
LimitUtpRate = true,
ListenPort = 31193,
Locale = "en",
Lsd = true,
MailNotificationAuthEnabled = false,
MailNotificationEmail = "",
MailNotificationEnabled = false,
MailNotificationPassword = "",
MailNotificationSender = "qBittorrentNotification@example.com",
MailNotificationSmtp = "smtp.changeme.com",
MailNotificationSslEnabled = false,
MailNotificationUsername = "",
MaxActiveDownloads = 3,
MaxActiveTorrents = 5,
MaxActiveUploads = 3,
MaxConnec = 500,
MaxConnecPerTorrent = 100,
MaxRatio = -1,
MaxRatioAct = 0,
MaxRatioEnabled = false,
MaxSeedingTime = -1,
MaxSeedingTimeEnabled = false,
MaxUploads = -1,
MaxUploadsPerTorrent = -1,
OutgoingPortsMax = 0,
OutgoingPortsMin = 0,
Pex = true,
PreallocateAll = false,
ProxyAuthEnabled = false,
ProxyIp = "0.0.0.0",
ProxyPassword = "",
ProxyPeerConnections = false,
ProxyPort = 8080,
ProxyTorrentsOnly = false,
ProxyType = 0,
ProxyUsername = "",
QueueingEnabled = false,
RandomPort = false,
RecheckCompletedTorrents = false,
ResolvePeerCountries = true,
RssAutoDownloadingEnabled = false,
RssMaxArticlesPerFeed = 50,
RssProcessingEnabled = false,
RssRefreshInterval = 30,
SavePath = "",
SavePathChangedTmmEnabled = false,
SaveResumeDataInterval = 60,
ScanDirs = new(),
ScheduleFromHour = 8,
ScheduleFromMin = 0,
ScheduleToHour = 20,
ScheduleToMin = 0,
SchedulerDays = 0,
SchedulerEnabled = false,
SendBufferLowWatermark = 10,
SendBufferWatermark = 500,
SendBufferWatermarkFactor = 50,
SlowTorrentDlRateThreshold = 2,
SlowTorrentInactiveTimer = 60,
SlowTorrentUlRateThreshold = 2,
SocketBacklogSize = 30,
StartPausedEnabled = false,
StopTrackerTimeout = 1,
TempPath = "",
TempPathEnabled = false,
TorrentChangedTmmEnabled = true,
UpLimit = 0,
UploadChokingAlgorithm = 1,
UploadSlotsBehavior = 0,
Upnp = true,
UpnpLeaseDuration = 0,
UseHttps = false,
UtpTcpMixedMode = 0,
WebUiAddress = "*",
WebUiBanDuration = 3600,
WebUiClickjackingProtectionEnabled = true,
WebUiCsrfProtectionEnabled = true,
WebUiDomainList = "*",
WebUiHostHeaderValidationEnabled = true,
WebUiHttpsCertPath = "",
WebUiHttpsKeyPath = "",
WebUiMaxAuthFailCount = 5,
WebUiPort = 8080,
WebUiSecureCookieEnabled = true,
WebUiSessionTimeout = 3600,
WebUiUpnp = false,
WebUiUsername = ""
};
var savePath = settings.DefaultSavePath;
preferences.SavePath = savePath;
preferences.TempPath = $"{savePath}temp{Path.DirectorySeparatorChar}";
var user = await authentication.GetUser();
if (user != null)
{
preferences.WebUiUsername = user.UserName;
}
return preferences;
}
public virtual async Task<IList<TorrentInfo>> TorrentInfo()
{
var savePath = settings.DefaultSavePath;
var results = new List<TorrentInfo>();
var allTorrents = await torrents.Get();
allTorrents = allTorrents.Where(m => m.Type == DownloadType.Torrent).ToList();
var prio = 0;
foreach (var torrent in allTorrents)
{
var downloadPath = savePath;
if (!String.IsNullOrWhiteSpace(torrent.Category))
{
downloadPath = Path.Combine(downloadPath, torrent.Category);
}
var torrentPath = downloadPath;
if (!String.IsNullOrWhiteSpace(torrent.RdName))
{
// Alldebrid stores single file torrents at the root folder.
if (torrent.ClientKind == Provider.AllDebrid && torrent.Files.Count == 1)
{
torrentPath = Path.Combine(downloadPath, torrent.Files[0].Path);
}
else
{
var existingContentPath = GetExistingContentPath(downloadPath, torrent);
if (!String.IsNullOrWhiteSpace(existingContentPath))
{
torrentPath = existingContentPath;
}
else
{
var contentPathName = GetContentPathName(torrent);
torrentPath = Path.Combine(downloadPath, contentPathName) + Path.DirectorySeparatorChar;
}
}
}
var rdProgress = Math.Clamp(torrent.RdProgress ?? 0.0, 0.0, 100.0) / 100.0;
var bytesTotal = torrent.RdSize ?? 0;
var speed = torrent.RdSpeed ?? 0;
var bytesDone = (Int64)(bytesTotal * rdProgress);
Double progress;
if (torrent.Completed != null)
{
progress = 1.0;
}
else if (torrent.Downloads is { Count: > 0 })
{
var dlStats = torrent.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
var dlBytesDone = dlStats.Sum(m => m.BytesDone);
var dlBytesTotal = dlStats.Sum(m => m.BytesTotal);
speed = (Int32)(dlStats.Any() ? dlStats.Average(m => m.Speed) : 0);
var downloadProgress = dlBytesTotal > 0 ? Math.Clamp((Double)dlBytesDone / dlBytesTotal, 0.0, 1.0) : 0;
if (rdProgress >= 1.0 && downloadProgress >= 1.0)
{
progress = 1.0;
}
else
{
progress = (rdProgress + downloadProgress) / 2.0;
}
}
else
{
progress = rdProgress;
}
var remaining = TimeSpan.Zero;
if (progress > 0 && progress < 1.0)
{
var startTime = torrent.Retry > torrent.Added ? torrent.Retry.Value : torrent.Added;
var elapsed = DateTimeOffset.UtcNow - startTime;
var totalEstimatedTime = TimeSpan.FromTicks((Int64)(elapsed.Ticks / progress));
remaining = totalEstimatedTime - elapsed;
}
var result = new TorrentInfo
{
AddedOn = torrent.Added.ToUnixTimeSeconds(),
AmountLeft = bytesTotal - bytesDone,
AutoTmm = false,
Availability = 2,
Category = torrent.Category ?? "",
Completed = bytesDone,
CompletionOn = torrent.Completed?.ToUnixTimeSeconds(),
ContentPath = torrentPath,
DlLimit = -1,
Dlspeed = speed,
Downloaded = bytesDone,
DownloadedSession = bytesDone,
Eta = (Int64)remaining.TotalSeconds,
FlPiecePrio = false,
ForceStart = false,
Hash = torrent.Hash,
LastActivity = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
MagnetUri = "",
MaxRatio = -1,
MaxSeedingTime = -1,
Name = torrent.RdName,
NumComplete = 10,
NumIncomplete = 0,
NumLeechs = 1,
NumSeeds = torrent.RdSeeders ?? 1,
Priority = ++prio,
Progress = (Single)progress,
Ratio = 1,
RatioLimit = 1,
SavePath = downloadPath,
SeedingTimeLimit = 1,
SeenComplete = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
SeqDl = false,
Size = bytesTotal,
State = "downloading",
SuperSeeding = false,
Tags = "",
TimeActive = (Int64)(DateTimeOffset.UtcNow - torrent.Added).TotalSeconds,
TotalSize = bytesTotal,
Tracker = "udp://tracker.opentrackr.org:1337",
UpLimit = -1,
Uploaded = bytesDone,
UploadedSession = bytesDone,
Upspeed = speed
};
if (!String.IsNullOrWhiteSpace(torrent.Error))
{
result.State = "error";
}
else if (torrent.Completed.HasValue)
{
result.State = "pausedUP";
}
else if (torrent.RdStatus == TorrentStatus.Downloading && torrent.RdSeeders < 1)
{
result.State = "stalledDL";
}
results.Add(result);
}
return results;
}
private static String GetContentPathName(Torrent torrent)
{
if (String.IsNullOrWhiteSpace(torrent.RdName))
{
return torrent.RdName ?? String.Empty;
}
var topLevelSelectedFiles = torrent.Files
.Where(m => m.Selected && !String.IsNullOrWhiteSpace(m.Path))
.Select(m => m.Path.Trim('/').Trim('\\'))
.Where(m => m.IndexOfAny(['/', '\\']) < 0)
.Select(Path.GetFileName)
.Where(m => !String.IsNullOrWhiteSpace(m))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (topLevelSelectedFiles.Count == 1)
{
var selectedFileName = topLevelSelectedFiles[0]!;
var selectedFileBaseName = Path.GetFileNameWithoutExtension(selectedFileName);
if (torrent.ClientKind == Provider.TorBox)
{
return selectedFileBaseName;
}
if (!String.IsNullOrWhiteSpace(selectedFileBaseName) &&
selectedFileBaseName.Equals(torrent.RdName, StringComparison.OrdinalIgnoreCase))
{
return selectedFileName;
}
}
return torrent.RdName;
}
private static String? GetExistingContentPath(String downloadPath, Torrent torrent)
{
if (!torrent.Completed.HasValue || !Directory.Exists(downloadPath))
{
return null;
}
var selectedFilePaths = torrent.Files
.Where(m => m.Selected && !String.IsNullOrWhiteSpace(m.Path))
.Select(m => NormalizeRelativePath(m.Path!))
.Where(m => !String.IsNullOrWhiteSpace(m))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var downloadFileNames = torrent.Downloads
.Select(m => m.FileName)
.Where(m => !String.IsNullOrWhiteSpace(m))
.Select(m => Path.GetFileName(m!))
.Where(m => !String.IsNullOrWhiteSpace(m))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (selectedFilePaths.Count == 0 && downloadFileNames.Count == 0)
{
return null;
}
foreach (var candidateRoot in GetCandidateContentRoots(downloadPath, torrent, selectedFilePaths, downloadFileNames))
{
if (IsMatchingContentRoot(candidateRoot, selectedFilePaths, downloadFileNames))
{
return candidateRoot + Path.DirectorySeparatorChar;
}
}
return null;
}
private static IEnumerable<String> GetCandidateContentRoots(String downloadPath,
Torrent torrent,
IEnumerable<String> selectedFilePaths,
IEnumerable<String> downloadFileNames)
{
var yielded = new HashSet<String>(StringComparer.OrdinalIgnoreCase);
void AddCandidate(ICollection<String> candidates, String? name)
{
if (!String.IsNullOrWhiteSpace(name))
{
candidates.Add(Path.Combine(downloadPath, name));
}
}
var directCandidates = new List<String>();
AddCandidate(directCandidates, torrent.RdName);
foreach (var fileName in downloadFileNames)
{
AddCandidate(directCandidates, fileName);
}
foreach (var selectedFilePath in selectedFilePaths)
{
AddCandidate(directCandidates, Path.GetFileName(selectedFilePath));
AddCandidate(directCandidates, GetFirstPathComponent(selectedFilePath));
}
foreach (var candidate in directCandidates)
{
if (Directory.Exists(candidate) && yielded.Add(candidate))
{
yield return candidate;
}
}
foreach (var directory in Directory.EnumerateDirectories(downloadPath))
{
if (yielded.Add(directory))
{
yield return directory;
}
}
}
private static Boolean IsMatchingContentRoot(String candidateRoot,
IEnumerable<String> selectedFilePaths,
IEnumerable<String> downloadFileNames)
{
foreach (var selectedFilePath in selectedFilePaths)
{
if (File.Exists(Path.Combine(candidateRoot, selectedFilePath)))
{
return true;
}
}
foreach (var fileName in downloadFileNames)
{
if (File.Exists(Path.Combine(candidateRoot, fileName)))
{
return true;
}
}
return false;
}
private static String NormalizeRelativePath(String path)
{
return path.Trim('/').Trim('\\').Replace('\\', Path.DirectorySeparatorChar);
}
private static String GetFirstPathComponent(String path)
{
var separatorIndex = path.IndexOfAny(['/', '\\']);
return separatorIndex < 0 ? path : path[..separatorIndex];
}
public async Task<IList<TorrentFileItem>?> TorrentFileContents(String hash)
{
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Torrent)
{
return null;
}
var progress = torrent.Completed.HasValue || torrent.RdStatus == TorrentStatus.Finished ? 1f : 0f;
return torrent.Files
.Select((file, index) => new TorrentFileItem
{
Index = index,
Name = file.Path,
Size = file.Bytes,
Progress = file.Selected ? progress : 0f,
Priority = file.Selected ? 1 : 0,
IsSeed = false
})
.ToList();
}
public async Task<TorrentProperties?> TorrentProperties(String hash)
{
var savePath = settings.DefaultSavePath;
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Torrent)
{
return null;
}
if (!String.IsNullOrWhiteSpace(torrent.Category))
{
savePath = Path.Combine(savePath, torrent.Category);
}
var bytesDone = torrent.RdProgress;
var bytesTotal = torrent.RdSize;
var speed = torrent.RdSpeed ?? 0;
if (torrent.Downloads.Count > 0)
{
var dlStats = torrent.Downloads.Select(m => torrents.GetDownloadStats(m.DownloadId)).ToList();
bytesDone = dlStats.Sum(m => m.BytesDone);
bytesTotal = dlStats.Sum(m => m.BytesTotal);
speed = (Int32)(dlStats.Any() ? dlStats.Average(m => m.Speed) : 0);
}
var result = new TorrentProperties
{
AdditionDate = torrent.Added.ToUnixTimeSeconds(),
Comment = "RealDebridClient <https://github.com/rogerfar/rdt-client>",
CompletionDate = torrent.Completed?.ToUnixTimeSeconds() ?? -1,
IsPrivate = false,
CreatedBy = "RealDebridClient <https://github.com/rogerfar/rdt-client>",
CreationDate = torrent.Added.ToUnixTimeSeconds(),
DlLimit = -1,
DlSpeed = speed,
DlSpeedAvg = speed,
Eta = 0,
LastSeen = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
NbConnections = 0,
NbConnectionsLimit = 100,
Peers = 0,
PeersTotal = 2,
PieceSize = bytesTotal,
PiecesHave = torrent.Downloads.Count(m => m.Completed.HasValue),
PiecesNum = torrent.Downloads.Count,
Reannounce = 0,
SavePath = savePath,
SeedingTime = 1,
Seeds = 100,
SeedsTotal = 100,
ShareRatio = 9999,
TimeElapsed = (Int64)(DateTimeOffset.UtcNow - torrent.Added).TotalSeconds,
TotalDownloaded = bytesDone,
TotalDownloadedSession = bytesDone,
TotalSize = bytesTotal,
TotalUploaded = bytesDone,
TotalUploadedSession = bytesDone,
TotalWasted = 0,
UpLimit = -1,
UpSpeed = speed,
UpSpeedAvg = speed
};
return result;
}
public async Task TorrentsFilePrio(String hash, IReadOnlyCollection<Int32> fileIds, Int32 priority)
{
await torrents.UpdateFileSelection(hash, fileIds, priority > 0);
}
public async Task TorrentsDelete(String hash, Boolean deleteFiles)
{
if (deleteFiles)
{
logger.LogDebug("Delete {hash}, with files", hash);
}
else
{
logger.LogDebug("Delete {hash}, no files", hash);
}
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Torrent)
{
return;
}
switch (settings.Current.Integrations.Default.FinishedAction)
{
case TorrentFinishedAction.RemoveAllTorrents:
logger.LogDebug("Removing torrents from debrid provider and RDT-Client, no files");
await torrents.Delete(torrent.TorrentId, true, true, deleteFiles);
break;
case TorrentFinishedAction.RemoveRealDebrid:
logger.LogDebug("Removing torrents from debrid provider, no files");
await torrents.Delete(torrent.TorrentId, false, true, deleteFiles);
break;
case TorrentFinishedAction.RemoveClient:
logger.LogDebug("Removing torrents from client, no files");
await torrents.Delete(torrent.TorrentId, true, false, deleteFiles);
break;
case TorrentFinishedAction.None:
logger.LogDebug("Not removing torrents or files");
break;
default:
logger.LogDebug($"Invalid torrent FinishedAction {torrent.FinishedAction}", torrent);
break;
}
}
public async Task<Torrent> TorrentsAddMagnet(String magnetLink, String? category, Int32? priority)
{
logger.LogDebug($"Add magnet {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)
};
return await torrents.AddMagnetToDebridQueue(magnetLink, torrent);
}
public async Task<Torrent> TorrentsAddFile(Byte[] fileBytes, 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 ? settings.Current.Integrations.Default.Priority : null)
};
return await torrents.AddFileToDebridQueue(fileBytes, torrent);
}
public async Task TorrentsSetCategory(String hash, String? category)
{
await torrents.UpdateCategory(hash, category);
}
public async Task<IDictionary<String, TorrentCategory>> TorrentsCategories()
{
var allTorrents = await torrents.Get();
var torrentsToGroup = allTorrents.Where(m => m.Type == DownloadType.Torrent && !String.IsNullOrWhiteSpace(m.Category))
.Select(m => m.Category!.ToLower())
.ToList();
var categoryList = (settings.Current.General.Categories ?? "")
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.Select(m => m.Trim())
.ToList();
torrentsToGroup.AddRange(categoryList);
var results = new Dictionary<String, TorrentCategory>();
if (torrentsToGroup.Count > 0)
{
results = torrentsToGroup.Distinct(StringComparer.CurrentCultureIgnoreCase)
.ToDictionary(m => m,
m => new TorrentCategory
{
Name = m,
SavePath = Path.Combine(settings.DefaultSavePath, m)
});
}
return results;
}
public async Task CategoryCreate(String? category)
{
if (category == null)
{
return;
}
category = category.Trim();
var categoriesSetting = settings.Current.General.Categories;
var categoryList = (categoriesSetting ?? "")
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.Select(m => m.Trim())
.ToList();
if (!categoryList.Contains(category))
{
categoryList.Add(category);
}
categoriesSetting = String.Join(",", categoryList);
await settings.Update("General:Categories", categoriesSetting);
}
public async Task CategoryRemove(String? category)
{
if (category == null)
{
return;
}
category = category.Trim();
var categoriesSetting = settings.Current.General.Categories;
var categoryList = (categoriesSetting ?? "")
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.Select(m => m.Trim())
.ToList();
categoryList = categoryList.Where(m => m != category).ToList();
categoriesSetting = String.Join(",", categoryList);
await settings.Update("General:Categories", categoriesSetting);
}
public async Task TorrentsTopPrio(String hash)
{
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Torrent)
{
return;
}
await torrents.UpdatePriority(hash, 1);
}
public async Task TorrentPause(String hash)
{
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Torrent)
{
return;
}
var downloadsForTorrent = await downloads.GetForTorrent(torrent.TorrentId);
foreach (var download in downloadsForTorrent)
{
if (runnerState.ActiveDownloadClients.TryGetValue(download.DownloadId, out var downloadClient))
{
await downloadClient.Pause();
}
}
}
public async Task TorrentResume(String hash)
{
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Torrent)
{
return;
}
var downloadsForTorrent = await downloads.GetForTorrent(torrent.TorrentId);
foreach (var download in downloadsForTorrent)
{
if (runnerState.ActiveDownloadClients.TryGetValue(download.DownloadId, out var downloadClient))
{
await downloadClient.Resume();
}
}
}
public async Task<SyncMetaData> SyncMainData()
{
var torrentInfo = await TorrentInfo();
var categories = await TorrentsCategories();
var activeDownloads = runnerState.ActiveDownloadClients.Sum(m => m.Value.Speed);
return new()
{
Categories = categories,
FullUpdate = true,
Rid = 0,
Tags = null,
Trackers = new Dictionary<String, List<String>>(),
Torrents = torrentInfo.ToDictionary(m => m.Hash, m => m),
ServerState = new()
{
DlInfoSpeed = activeDownloads,
UpInfoSpeed = 0
}
};
}
public virtual TransferInfo TransferInfo()
{
var activeDownloads = runnerState.ActiveDownloadClients.Sum(m => m.Value.Speed);
return new()
{
ConnectionStatus = "connected",
DlInfoData = DownloadClient.GetTotalBytesDownloadedThisSession(),
DlInfoSpeed = activeDownloads,
DlRateLimit = settings.Current.DownloadClient.MaxSpeed
};
}
public async Task<List<Tracker>> TorrentsTrackers(String hash)
{
var torrent = await torrents.GetByHash(hash);
if (torrent == null || torrent.Type != DownloadType.Torrent)
{
return [];
}
return
[
new()
{
Url = $"http://{torrent.RdHost ?? torrent.ClientKind.ToString()}/**".ToLower(),
Status = "Working",
NumPeers = torrent.RdSeeders ?? 1,
Msg = $"Fake Tracker for {torrent.ClientKind}"
}
];
}
}