diff --git a/CHANGELOG.md b/CHANGELOG.md index 88b2cc2..da04f8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.90] - 2024-12-06 +### Changed +- Download individual files from Torbox instead of a zip file. + +### Removed +- Removed ability to select instant files from AllDebrid. + +## [2.0.89] - 2024-11-24 +### Changed +- Disabled selecting of files as Real-Debrid was the only provider that supported that. + +## [2.0.88] - 2024-11-24 +### Changed +- Catch disabled instant availability endpoint from Real Debrid. + +## [2.0.87] - 2024-11-18 +### Added +- Torbox support. +- qBittorrent API authentication when no authentication is used. +### Changed +- .NET version changed to .NET 9. +- Changed download limit to be split by active torrents. + ## [2.0.86] - 2024-09-03 ### Changed - Add potential fix for BASE_PATH. diff --git a/Dockerfile b/Dockerfile index 9bc6a22..e210c88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ RUN \ RUN ls -FCla /appclient/root # Stage 2 - Build the backend -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 AS dotnet-build-env +FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS dotnet-build-env ARG TARGETPLATFORM ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/amd64} ARG BUILDPLATFORM @@ -66,14 +66,14 @@ RUN \ RUN \ if [ "$TARGETPLATFORM" = "linux/arm/v7" ] ; then \ - wget https://download.visualstudio.microsoft.com/download/pr/c3bf3103-efdb-42e0-af55-bbf861a4215b/dc22eda8877933b8c6569e3823f18d21/aspnetcore-runtime-8.0.0-linux-musl-arm64.tar.gz && \ - tar zxf aspnetcore-runtime-8.0.0-linux-musl-arm64.tar.gz -C /usr/share/dotnet ; \ + wget https://download.visualstudio.microsoft.com/download/pr/59a041e1-921e-405e-8092-95333f80f9ca/63e83e3feb70e05ca05ed5db3c579be2/aspnetcore-runtime-9.0.0-linux-musl-arm.tar.gz && \ + tar zxf aspnetcore-runtime-9.0.0-linux-musl-arm.tar.gz -C /usr/share/dotnet ; \ elif [ "$TARGETPLATFORM" = "linux/arm64" ] ; then \ - wget https://download.visualstudio.microsoft.com/download/pr/c3bf3103-efdb-42e0-af55-bbf861a4215b/dc22eda8877933b8c6569e3823f18d21/aspnetcore-runtime-8.0.0-linux-musl-arm64.tar.gz && \ - tar zxf aspnetcore-runtime-8.0.0-linux-musl-arm64.tar.gz -C /usr/share/dotnet ; \ + wget https://download.visualstudio.microsoft.com/download/pr/e137f557-83cb-4f55-b1c8-e5f59ccd3cba/b8ba6f2ab96d0961757b71b00c201f31/aspnetcore-runtime-9.0.0-linux-musl-arm64.tar.gz && \ + tar zxf aspnetcore-runtime-9.0.0-linux-musl-arm64.tar.gz -C /usr/share/dotnet ; \ else \ - wget https://download.visualstudio.microsoft.com/download/pr/7aa33fc7-07fe-48c2-8e44-a4bfb4928535/3b96ec50970eee414895ef3a5b188bcd/aspnetcore-runtime-8.0.0-linux-musl-x64.tar.gz && \ - tar zxf aspnetcore-runtime-8.0.0-linux-musl-x64.tar.gz -C /usr/share/dotnet ; \ + wget https://download.visualstudio.microsoft.com/download/pr/86d7a513-fe71-4f37-b9ec-fdcf5566cce8/e72574fc82d7496c73a61f411d967d8e/aspnetcore-runtime-9.0.0-linux-musl-x64.tar.gz && \ + tar zxf aspnetcore-runtime-9.0.0-linux-musl-x64.tar.gz -C /usr/share/dotnet ; \ fi RUN \ diff --git a/README.md b/README.md index 9756351..22eaffc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is a web interface to manage your torrents on Real-Debrid, AllDebrid or Pre - Download all files from Real-Debrid, AllDebrid or Premiumize to your local machine automatically - Unpack all files when finished downloading - Implements a fake qBittorrent API so you can hook up other applications like Sonarr, Radarr or Couchpotato. -- Built with Angular 15 and .NET 8 +- Built with Angular 15 and .NET 9 **You will need a Premium service at Real-Debrid, AllDebrid or Premiumize!** @@ -30,7 +30,7 @@ Instead of running in Docker you can install it as a service in Windows or Linux ## Windows Service -1. Make sure you have the ASP.NET Core Runtime 8 installed: [https://dotnet.microsoft.com/download/dotnet/8.0](https://dotnet.microsoft.com/download/dotnet/8.0) +1. Make sure you have the **ASP.NET Core Runtime 9.0.0** installed: [https://dotnet.microsoft.com/download/dotnet/9.0](https://dotnet.microsoft.com/download/dotnet/9.0) 2. Get the latest zip file from the Releases page and extract it to your host. 3. Open the `appsettings.json` file and replace the `LogLevel` `Path` to a path on your host. 4. In `appsettings.json` replace the `Database` `Path` to a path on your host. @@ -52,7 +52,7 @@ Instead of running in Docker you can install it as a service in Linux. ```rm packages-microsoft-prod.deb``` - ```sudo apt-get update && sudo apt-get install -y dotnet-sdk-8.0``` + ```sudo apt-get update && sudo apt-get install -y dotnet-sdk-9.0``` 2. Get latest archive from [releases](https://github.com/rogerfar/rdt-client/releases): ```wget ``` @@ -188,7 +188,7 @@ By default the application runs in the root of your hosted address (i.e. https:/ - NodeJS - NPM - Angular CLI -- .NET 8 +- .NET 9 - Visual Studio 2022 - (optional) Resharper diff --git a/client/src/app/add-new-torrent/add-new-torrent.component.html b/client/src/app/add-new-torrent/add-new-torrent.component.html index 7d939e6..12df392 100644 --- a/client/src/app/add-new-torrent/add-new-torrent.component.html +++ b/client/src/app/add-new-torrent/add-new-torrent.component.html @@ -72,35 +72,6 @@

-
- -
- -
-

When a torrent is fully downloaded on the provider, perform this action.

-

- This option is only available for RealDebrid. AllDebrid and Premiumize will always download the full torrent. -

-
- -
- This torrent is available for immediate download. -
- -
- This torrent is not available for immediate download. -
-
@@ -221,39 +192,6 @@

-
-
- -

- These files are available for immediate download from your debrid provider.
- It is possible that there are more files in the torrent, which are not shown here.
-

-
-
- -
-
- -
-
- -
-
-
-
diff --git a/client/src/app/navbar/navbar.component.html b/client/src/app/navbar/navbar.component.html index 8395feb..55ee2b7 100644 --- a/client/src/app/navbar/navbar.component.html +++ b/client/src/app/navbar/navbar.component.html @@ -55,7 +55,7 @@ Profile Logout - Version 2.0.86 + Version 2.0.90
diff --git a/client/src/app/navbar/navbar.component.ts b/client/src/app/navbar/navbar.component.ts index c47e2b9..95c8067 100644 --- a/client/src/app/navbar/navbar.component.ts +++ b/client/src/app/navbar/navbar.component.ts @@ -15,7 +15,11 @@ export class NavbarComponent implements OnInit { public profile: Profile; public providerLink: string; - constructor(private settingsService: SettingsService, private authService: AuthService, private router: Router) {} + constructor( + private settingsService: SettingsService, + private authService: AuthService, + private router: Router, + ) {} ngOnInit(): void { this.settingsService.getProfile().subscribe((result) => { @@ -31,6 +35,9 @@ export class NavbarComponent implements OnInit { case 'Premiumize': this.providerLink = 'https://www.premiumize.me/'; break; + case 'TorBox': + this.providerLink = 'https://torbox.app/'; + break; } }); } @@ -40,7 +47,7 @@ export class NavbarComponent implements OnInit { () => { this.router.navigate(['/login']); }, - (err) => {} + (err) => {}, ); } } diff --git a/client/src/app/setup/setup.component.html b/client/src/app/setup/setup.component.html index 3de49e5..643e618 100644 --- a/client/src/app/setup/setup.component.html +++ b/client/src/app/setup/setup.component.html @@ -42,7 +42,7 @@
To be able to use the Real-Debrid Client you need a premium subscription to download torrents.

- Not premium yet? You have the choice of 2 providers: + Not premium yet? You have the choice of 4 providers:
Use this link to sign up to Real-Debrid.Use this link to sign up to Premiumize. + Use this link to sign up to TorBox.
(Referal links)
@@ -65,6 +67,7 @@ + @@ -91,6 +94,10 @@ >https://www.premiumize.me/account.

+

+ You can find your API key here: + https://torbox.app/settings. +

diff --git a/package.json b/package.json index 4ea720f..8c177b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rdt-client", - "version": "2.0.86", + "version": "2.0.90", "description": "This is a web interface to manage your torrents on Real-Debrid.", "main": "index.js", "dependencies": { diff --git a/server/RdtClient.Data/Data/TorrentData.cs b/server/RdtClient.Data/Data/TorrentData.cs index 63d6cf6..34f43a3 100644 --- a/server/RdtClient.Data/Data/TorrentData.cs +++ b/server/RdtClient.Data/Data/TorrentData.cs @@ -51,12 +51,12 @@ public class TorrentData(DataContext dataContext) public async Task GetByHash(String hash) { -#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons + hash = hash.ToLower(); + var dbTorrent = await dataContext.Torrents .AsNoTracking() .Include(m => m.Downloads) - .FirstOrDefaultAsync(m => m.Hash.ToLower() == hash.ToLower()); -#pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons + .FirstOrDefaultAsync(m => m.Hash == hash); if (dbTorrent == null) { diff --git a/server/RdtClient.Data/Enums/Provider.cs b/server/RdtClient.Data/Enums/Provider.cs index 7422663..ff372dc 100644 --- a/server/RdtClient.Data/Enums/Provider.cs +++ b/server/RdtClient.Data/Enums/Provider.cs @@ -11,5 +11,8 @@ public enum Provider AllDebrid, [Description("Premiumize")] - Premiumize + Premiumize, + + [Description("TorBox")] + TorBox } \ No newline at end of file diff --git a/server/RdtClient.Data/Models/Internal/DbSettings.cs b/server/RdtClient.Data/Models/Internal/DbSettings.cs index 21a0f34..eb62b6b 100644 --- a/server/RdtClient.Data/Models/Internal/DbSettings.cs +++ b/server/RdtClient.Data/Models/Internal/DbSettings.cs @@ -155,10 +155,11 @@ http://127.0.0.1:6800/jsonrpc.")] public class DbSettingsProvider { [DisplayName("Provider")] - [Description(@"The following 3 providers are supported: + [Description(@"The following 4 providers are supported: https://real-debrid.com https://alldebrid.com https://www.premiumize.me/ +https://torbox.app/ At this point only 1 provider can be used at the time.")] public Provider Provider { get; set; } = Provider.RealDebrid; @@ -168,7 +169,9 @@ At this point only 1 provider can be used at the time.")] or https://alldebrid.com/apikeys/ or -https://www.premiumize.me/account/")] +https://www.premiumize.me/account/ +or +https://torbox.app/settings/")] public String ApiKey { get; set; } = ""; [DisplayName("Automatically import and process torrents added to provider")] diff --git a/server/RdtClient.Data/RdtClient.Data.csproj b/server/RdtClient.Data/RdtClient.Data.csproj index 81e9430..aebafe5 100644 --- a/server/RdtClient.Data/RdtClient.Data.csproj +++ b/server/RdtClient.Data/RdtClient.Data.csproj @@ -1,22 +1,22 @@ - net8.0 + net9.0 enable enable latest - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/server/RdtClient.Service/DiConfig.cs b/server/RdtClient.Service/DiConfig.cs index e5f7aa2..695443f 100644 --- a/server/RdtClient.Service/DiConfig.cs +++ b/server/RdtClient.Service/DiConfig.cs @@ -24,6 +24,7 @@ public static class DiConfig services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/server/RdtClient.Service/RdtClient.Service.csproj b/server/RdtClient.Service/RdtClient.Service.csproj index 578fdd6..30f2067 100644 --- a/server/RdtClient.Service/RdtClient.Service.csproj +++ b/server/RdtClient.Service/RdtClient.Service.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable latest @@ -11,17 +11,18 @@ - + - - + + - + - + + diff --git a/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs b/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs index 98cc1ba..11e02da 100644 --- a/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs +++ b/server/RdtClient.Service/Services/Downloaders/InternalDownloader.cs @@ -26,7 +26,7 @@ public class InternalDownloader : IDownloader _uri = uri; _filePath = filePath; - + _downloadConfiguration = new(); SetSettings(); @@ -131,14 +131,14 @@ public class InternalDownloader : IDownloader { settingDownloadParallelCount = 1; } - + var settingDownloadMaxSpeed = Settings.Get.DownloadClient.MaxSpeed; if (settingDownloadMaxSpeed <= 0) { settingDownloadMaxSpeed = 0; } - + settingDownloadMaxSpeed /= Math.Max(TorrentRunner.ActiveDownloadClients.Count, 1); settingDownloadMaxSpeed = settingDownloadMaxSpeed * 1024 * 1024; var settingDownloadTimeout = Settings.Get.DownloadClient.Timeout; diff --git a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs index a46e3fe..bc0102c 100644 --- a/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/AllDebridTorrentClient.cs @@ -118,23 +118,9 @@ public class AllDebridTorrentClient(ILogger logger, IHtt return resultId; } - public async Task> GetAvailableFiles(String hash) + public Task> GetAvailableFiles(String hash) { - var isAvailable = await GetClient().Magnet.InstantAvailabilityAsync(hash); - - if (isAvailable) - { - return - [ - new() - { - Filename = "All files", - Filesize = 0 - } - ]; - } - - return []; + return Task.FromResult>([]); } public Task SelectFiles(Torrent torrent) diff --git a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs index b7e8382..cb170c2 100644 --- a/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/PremiumizeTorrentClient.cs @@ -117,8 +117,7 @@ public class PremiumizeTorrentClient(ILogger logger, IH public Task> GetAvailableFiles(String hash) { - var result = new List(); - return Task.FromResult>(result); + return Task.FromResult>([]); } public Task SelectFiles(Torrent torrent) diff --git a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs index 2b09057..9cc8ad0 100644 --- a/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs +++ b/server/RdtClient.Service/Services/TorrentClients/RealDebridTorrentClient.cs @@ -137,49 +137,27 @@ public class RealDebridTorrentClient(ILogger logger, IH return result.Id; } - public async Task> GetAvailableFiles(String hash) + public Task> GetAvailableFiles(String hash) { - var result = await GetClient().Torrents.GetAvailableFiles(hash); - - var files = result.SelectMany(m => m.Value).SelectMany(m => m.Value).SelectMany(m => m.Values); - - var groups = files.Where(m => m.Filename != null).GroupBy(m => $"{m.Filename}-{m.Filesize}"); - - var torrentClientAvailableFiles = groups.Select(m => new TorrentClientAvailableFile - { - Filename = m.First().Filename!, - Filesize = m.First().Filesize - }).ToList(); - - return torrentClientAvailableFiles; + return Task.FromResult>([]); } public async Task SelectFiles(Data.Models.Data.Torrent torrent) { - var files = torrent.Files; + IList files; Log("Seleting files", torrent); - if (torrent.DownloadAction == TorrentDownloadAction.DownloadAvailableFiles) - { - Log($"Determining which files are already available on RealDebrid", torrent); - - var availableFiles = await GetAvailableFiles(torrent.Hash); - - Log($"Found {files.Count}/{torrent.Files.Count} available files on RealDebrid", torrent); - - files = torrent.Files.Where(m => availableFiles.Any(f => m.Path.EndsWith(f.Filename))).ToList(); - } - else if (torrent.DownloadAction == TorrentDownloadAction.DownloadAll) - { - Log("Selecting all files", torrent); - files = [.. torrent.Files]; - } - else if (torrent.DownloadAction == TorrentDownloadAction.DownloadManual) + if (torrent.DownloadAction == TorrentDownloadAction.DownloadManual) { Log("Selecting manual selected files", torrent); files = torrent.Files.Where(m => torrent.ManualFiles.Any(f => m.Path.EndsWith(f))).ToList(); } + else + { + Log("Selecting all files", torrent); + files = [.. torrent.Files]; + } Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent); diff --git a/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs new file mode 100644 index 0000000..088c928 --- /dev/null +++ b/server/RdtClient.Service/Services/TorrentClients/TorBoxTorrentClient.cs @@ -0,0 +1,319 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using TorBoxNET; +using RdtClient.Data.Enums; +using RdtClient.Data.Models.TorrentClient; + +namespace RdtClient.Service.Services.TorrentClients; + +public class TorBoxTorrentClient(ILogger logger, IHttpClientFactory httpClientFactory) : ITorrentClient +{ + private TimeSpan? _offset; + private TorBoxNetClient GetClient() + { + try + { + var apiKey = Settings.Get.Provider.ApiKey; + + if (String.IsNullOrWhiteSpace(apiKey)) + { + throw new("TorBox API Key not set in the settings"); + } + + var httpClient = httpClientFactory.CreateClient(); + httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout); + + var torBoxNetClient = new TorBoxNetClient(null, httpClient, 5); + torBoxNetClient.UseApiAuthentication(apiKey); + + // Get the server time to fix up the timezones on results + if (_offset == null) + { + var serverTime = DateTimeOffset.UtcNow; + _offset = serverTime.Offset; + } + + return torBoxNetClient; + } + catch (AggregateException ae) + { + foreach (var inner in ae.InnerExceptions) + { + logger.LogError(inner, $"The connection to RealDebrid has failed: {inner.Message}"); + } + + throw; + } + catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) + { + logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}"); + + throw; + } + catch (TaskCanceledException ex) + { + logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}"); + + throw; + } + } + + private TorrentClientTorrent Map(TorrentInfoResult torrent) + { + return new() + { + Id = torrent.Hash, + Filename = torrent.Name, + OriginalFilename = torrent.Name, + Hash = torrent.Hash, + Bytes = torrent.Size, + OriginalBytes = torrent.Size, + Host = torrent.DownloadPresent.ToString(), + Split = 0, + Progress = (Int64)((torrent.Progress) * 100.0), + Status = torrent.DownloadState, + Added = ChangeTimeZone(torrent.CreatedAt)!.Value, + Files = (torrent.Files ?? []).Select(m => new TorrentClientFile + { + Path = String.Join("/", m.Name.Split('/').Skip(1)), + Bytes = m.Size, + Id = m.Id, + Selected = true + }).ToList(), + Links = [], + Ended = ChangeTimeZone(torrent.UpdatedAt), + Speed = torrent.DownloadSpeed, + Seeders = torrent.Seeds, + }; + } + + public async Task> GetTorrents() + { + var torrents = new List(); + + var currentTorrents = await GetClient().Torrents.GetCurrentAsync(true); + if (currentTorrents != null) + { + torrents.AddRange(currentTorrents); + } + + var queuedTorrents = await GetClient().Torrents.GetQueuedAsync(true); + if (queuedTorrents != null) + { + torrents.AddRange(queuedTorrents); + } + + return torrents.Select(Map).ToList(); + } + + public async Task GetUser() + { + var user = await GetClient().User.GetAsync(false); + + return new() + { + Username = user.Data!.Email, + Expiration = user.Data!.Plan != 0 ? user.Data!.PremiumExpiresAt!.Value : null + }; + } + + public async Task AddMagnet(String magnetLink) + { + // var seeding = Settings.Get.Integrations.Default.FinishedAction; + + // Line is not working right now, will disable seeding and fix in december when I have time again. + // var seed = (seeding == TorrentFinishedAction.RemoveAllTorrents || seeding == TorrentFinishedAction.RemoveRealDebrid) ? 3 : 2; + + var seed = 3; + var result = await GetClient().Torrents.AddMagnetAsync(magnetLink, seed, false); + + if (result.Error == "ACTIVE_LIMIT") + { + var magnetLinkInfo = MonoTorrent.MagnetLink.Parse(magnetLink); + return magnetLinkInfo.InfoHashes.V1!.ToHex().ToLowerInvariant(); + } + else + { + return result.Data!.Hash!; + } + } + + public async Task AddFile(Byte[] bytes) + { + // Line is not working right now, will disable seeding and fix in december when I have time again. + // var seed = (seeding == TorrentFinishedAction.RemoveAllTorrents || seeding == TorrentFinishedAction.RemoveRealDebrid) ? 3 : 2; + const Int32 seed = 3; + + var result = await GetClient().Torrents.AddFileAsync(bytes, seed); + if (result.Error == "ACTIVE_LIMIT") + { + using var stream = new MemoryStream(bytes); + + var torrent = await MonoTorrent.Torrent.LoadAsync(stream); + return torrent.InfoHashes.V1!.ToHex().ToLowerInvariant(); + } + + return result.Data!.Hash!; + } + + public async Task> GetAvailableFiles(String hash) + { + var availability = await GetClient().Torrents.GetAvailabilityAsync(hash, listFiles: true); + + if (availability.Data != null) + { + return (availability.Data[0]?.Files ?? []).Select(file => new TorrentClientAvailableFile + { + Filename = file.Name, + Filesize = file.Size + }) + .ToList(); + } + + return []; + } + + public Task SelectFiles(Data.Models.Data.Torrent torrent) + { + return Task.CompletedTask; + } + + public async Task Delete(String torrentId) + { + await GetClient().Torrents.ControlAsync(torrentId, "delete"); + } + + public async Task Unrestrict(String link) + { + var segments = link.Split('/'); + + var zipped = segments[5] == "zip"; + var fileId = zipped ? "0" : segments[5]; + + var result = await GetClient().Torrents.RequestDownloadAsync(Convert.ToInt32(segments[4]), Convert.ToInt32(fileId), zipped); + + if (result.Error != null) + { + throw new("Unrestrict returned an invalid download"); + } + + return result.Data!; + } + + public async Task UpdateData(Data.Models.Data.Torrent torrent, TorrentClientTorrent? torrentClientTorrent) + { + try + { + if (torrent.RdId == null) + { + return torrent; + } + + var rdTorrent = await GetInfo(torrent.Hash) ?? throw new($"Resource not found"); + + if (!String.IsNullOrWhiteSpace(rdTorrent.Filename)) + { + torrent.RdName = rdTorrent.Filename; + } + + if (!String.IsNullOrWhiteSpace(rdTorrent.OriginalFilename)) + { + torrent.RdName = rdTorrent.OriginalFilename; + } + + if (rdTorrent.Bytes > 0) + { + torrent.RdSize = rdTorrent.Bytes; + } + else if (rdTorrent.OriginalBytes > 0) + { + torrent.RdSize = rdTorrent.OriginalBytes; + } + + if (rdTorrent.Files != null) + { + torrent.RdFiles = JsonConvert.SerializeObject(rdTorrent.Files); + } + + torrent.RdHost = rdTorrent.Host; + torrent.RdSplit = rdTorrent.Split; + torrent.RdProgress = rdTorrent.Progress; + torrent.RdAdded = rdTorrent.Added; + torrent.RdEnded = rdTorrent.Ended; + torrent.RdSpeed = rdTorrent.Speed; + torrent.RdSeeders = rdTorrent.Seeders; + torrent.RdStatusRaw = rdTorrent.Status; + + if (rdTorrent.Host == "True") + { + torrent.RdStatus = TorrentStatus.Finished; + } + else + { + torrent.RdStatus = rdTorrent.Status switch + { + "queued" => TorrentStatus.Processing, + "metaDL" => TorrentStatus.Processing, + "checking" => TorrentStatus.Processing, + "checkingResumeData" => TorrentStatus.Processing, + "paused" => TorrentStatus.Downloading, + "downloading" => TorrentStatus.Downloading, + "completed" => TorrentStatus.Downloading, + "uploading" => TorrentStatus.Downloading, + "uploading (no peers)" => TorrentStatus.Downloading, + "stalled" => TorrentStatus.Downloading, + "stalled (no seeds)" => TorrentStatus.Downloading, + "processing" => TorrentStatus.Downloading, + "cached" => TorrentStatus.Finished, + _ => TorrentStatus.Error + }; + } + + } + catch (Exception ex) + { + if (ex.Message == "Resource not found") + { + torrent.RdStatusRaw = "deleted"; + } + else + { + throw; + } + } + + return torrent; + } + + public async Task?> GetDownloadLinks(Data.Models.Data.Torrent torrent) + { + var files = new List(); + + var torrentId = await GetClient().Torrents.GetHashInfoAsync(torrent.Hash, skipCache: true); + + foreach (var file in torrent.Files) + { + var newFile = $"https://torbox.app/fakedl/{torrentId?.Id}/{file.Id}"; + files.Add(newFile); + } + + return files; + } + + private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset) + { + if (_offset == null) + { + return dateTimeOffset; + } + + return dateTimeOffset?.Subtract(_offset.Value).ToOffset(_offset.Value); + } + + private async Task GetInfo(String torrentHash) + { + var result = await GetClient().Torrents.GetHashInfoAsync(torrentHash, skipCache: true); + + return Map(result!); + } +} diff --git a/server/RdtClient.Service/Services/Torrents.cs b/server/RdtClient.Service/Services/Torrents.cs index d1ff70d..25817cf 100644 --- a/server/RdtClient.Service/Services/Torrents.cs +++ b/server/RdtClient.Service/Services/Torrents.cs @@ -22,7 +22,8 @@ public class Torrents( Downloads downloads, AllDebridTorrentClient allDebridTorrentClient, PremiumizeTorrentClient premiumizeTorrentClient, - RealDebridTorrentClient realDebridTorrentClient) + RealDebridTorrentClient realDebridTorrentClient, + TorBoxTorrentClient torBoxTorrentClient) { private static readonly SemaphoreSlim RealDebridUpdateLock = new(1, 1); @@ -40,6 +41,7 @@ public class Torrents( Provider.Premiumize => premiumizeTorrentClient, Provider.RealDebrid => realDebridTorrentClient, Provider.AllDebrid => allDebridTorrentClient, + Provider.TorBox => torBoxTorrentClient, _ => throw new("Invalid Provider") }; } diff --git a/server/RdtClient.Web/Controllers/QBittorrentController.cs b/server/RdtClient.Web/Controllers/QBittorrentController.cs index 4fdfd77..fe162b2 100644 --- a/server/RdtClient.Web/Controllers/QBittorrentController.cs +++ b/server/RdtClient.Web/Controllers/QBittorrentController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using RdtClient.Data.Enums; using RdtClient.Data.Models.QBittorrent; using RdtClient.Service.Services; @@ -21,6 +22,11 @@ public class QBittorrentController(ILogger logger, QBitto { 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."); diff --git a/server/RdtClient.Web/Program.cs b/server/RdtClient.Web/Program.cs index e65ad0c..d1d676a 100644 --- a/server/RdtClient.Web/Program.cs +++ b/server/RdtClient.Web/Program.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Net; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Hosting.WindowsServices; @@ -136,8 +135,6 @@ builder.Host.UseWindowsService(); RdtClient.Data.DiConfig.Config(builder.Services, appSettings); builder.Services.RegisterRdtServices(); -ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; - try { // Build the app diff --git a/server/RdtClient.Web/RdtClient.Web.csproj b/server/RdtClient.Web/RdtClient.Web.csproj index 9de70e8..adfbda3 100644 --- a/server/RdtClient.Web/RdtClient.Web.csproj +++ b/server/RdtClient.Web/RdtClient.Web.csproj @@ -1,10 +1,10 @@ - net8.0 + net9.0 Exe 94c24cba-f03f-4453-a671-3640b517c573 - 2.0.86 + 2.0.90 enable enable latest @@ -29,17 +29,17 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + diff --git a/server/RdtClient.sln.DotSettings b/server/RdtClient.sln.DotSettings index b248b47..46b2271 100644 --- a/server/RdtClient.sln.DotSettings +++ b/server/RdtClient.sln.DotSettings @@ -199,10 +199,12 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True True True True True True True + True \ No newline at end of file