Merge remote-tracking branch 'rogerfar/master'
This commit is contained in:
commit
f9335be163
24 changed files with 435 additions and 163 deletions
23
CHANGELOG.md
23
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/),
|
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).
|
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
|
## [2.0.86] - 2024-09-03
|
||||||
### Changed
|
### Changed
|
||||||
- Add potential fix for BASE_PATH.
|
- Add potential fix for BASE_PATH.
|
||||||
|
|
|
||||||
14
Dockerfile
14
Dockerfile
|
|
@ -21,7 +21,7 @@ RUN \
|
||||||
RUN ls -FCla /appclient/root
|
RUN ls -FCla /appclient/root
|
||||||
|
|
||||||
# Stage 2 - Build the backend
|
# 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
|
ARG TARGETPLATFORM
|
||||||
ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/amd64}
|
ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/amd64}
|
||||||
ARG BUILDPLATFORM
|
ARG BUILDPLATFORM
|
||||||
|
|
@ -66,14 +66,14 @@ RUN \
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
if [ "$TARGETPLATFORM" = "linux/arm/v7" ] ; then \
|
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 && \
|
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-8.0.0-linux-musl-arm64.tar.gz -C /usr/share/dotnet ; \
|
tar zxf aspnetcore-runtime-9.0.0-linux-musl-arm.tar.gz -C /usr/share/dotnet ; \
|
||||||
elif [ "$TARGETPLATFORM" = "linux/arm64" ] ; then \
|
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 && \
|
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-8.0.0-linux-musl-arm64.tar.gz -C /usr/share/dotnet ; \
|
tar zxf aspnetcore-runtime-9.0.0-linux-musl-arm64.tar.gz -C /usr/share/dotnet ; \
|
||||||
else \
|
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 && \
|
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-8.0.0-linux-musl-x64.tar.gz -C /usr/share/dotnet ; \
|
tar zxf aspnetcore-runtime-9.0.0-linux-musl-x64.tar.gz -C /usr/share/dotnet ; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
|
|
|
||||||
|
|
@ -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
|
- Download all files from Real-Debrid, AllDebrid or Premiumize to your local machine automatically
|
||||||
- Unpack all files when finished downloading
|
- Unpack all files when finished downloading
|
||||||
- Implements a fake qBittorrent API so you can hook up other applications like Sonarr, Radarr or Couchpotato.
|
- 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!**
|
**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
|
## 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.
|
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.
|
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.
|
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```
|
```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):
|
2. Get latest archive from [releases](https://github.com/rogerfar/rdt-client/releases):
|
||||||
```wget <zip_url>```
|
```wget <zip_url>```
|
||||||
|
|
@ -188,7 +188,7 @@ By default the application runs in the root of your hosted address (i.e. https:/
|
||||||
- NodeJS
|
- NodeJS
|
||||||
- NPM
|
- NPM
|
||||||
- Angular CLI
|
- Angular CLI
|
||||||
- .NET 8
|
- .NET 9
|
||||||
- Visual Studio 2022
|
- Visual Studio 2022
|
||||||
- (optional) Resharper
|
- (optional) Resharper
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,35 +72,6 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Post Torrent Download Action</label>
|
|
||||||
<div class="control select is-fullwidth">
|
|
||||||
<select [(ngModel)]="downloadAction" [disabled]="provider === 'AllDebrid' || provider === 'Premiumize'">
|
|
||||||
<option [ngValue]="0">Download all files</option>
|
|
||||||
<option [ngValue]="1">Download all available files</option>
|
|
||||||
<option [ngValue]="2">Manually pick files</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<p class="help">When a torrent is fully downloaded on the provider, perform this action.</p>
|
|
||||||
<p class="help" *ngIf="provider === 'AllDebrid'">
|
|
||||||
This option is only available for RealDebrid. AllDebrid and Premiumize will always download the full torrent.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="notification is-success is-light"
|
|
||||||
*ngIf="provider === 'AllDebrid' && availableFiles && availableFiles.length > 0"
|
|
||||||
>
|
|
||||||
This torrent is available for immediate download.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="notification is-warning is-light"
|
|
||||||
*ngIf="provider === 'AllDebrid' && availableFiles && availableFiles.length === 0"
|
|
||||||
>
|
|
||||||
This torrent is not available for immediate download.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Minimum file size to download</label>
|
<label class="label">Minimum file size to download</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
|
@ -221,39 +192,6 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div fxFlex *ngIf="provider === 'RealDebrid'">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Available files</label>
|
|
||||||
<p class="help">
|
|
||||||
These files are available for immediate download from your debrid provider. <br />
|
|
||||||
It is possible that there are more files in the torrent, which are not shown here.<br />
|
|
||||||
</p>
|
|
||||||
<div class="scroll-container">
|
|
||||||
<div class="field" *ngIf="downloadAction === 2">
|
|
||||||
<label class="checkbox is-fullwidth-label">
|
|
||||||
<input type="checkbox" [checked]="allSelected" (change)="downloadFileCheckedAll()" />
|
|
||||||
Select all
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="field" *ngIf="downloadAction === 2 && availableFiles !== null">
|
|
||||||
<label class="checkbox is-fullwidth-label" *ngFor="let file of availableFiles">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
[checked]="downloadFiles[file.filename]"
|
|
||||||
(change)="downloadFileChecked(file.filename)"
|
|
||||||
/>
|
|
||||||
{{ file.filename }} ({{ file.filesize | filesize }})
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="field" *ngIf="downloadAction !== 2 && availableFiles !== null">
|
|
||||||
<label class="is-fullwidth-label is-block" *ngFor="let file of availableFiles">
|
|
||||||
<span [ngClass]="{ 'strike-through': isRegexExcluded(file) }">{{ file.filename }}</span>
|
|
||||||
<span *ngIf="file.filesize > 0">({{ file.filesize | filesize }})</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
<a class="navbar-item" routerLink="profile"> Profile </a>
|
<a class="navbar-item" routerLink="profile"> Profile </a>
|
||||||
<a class="navbar-item" (click)="logout()"> Logout </a>
|
<a class="navbar-item" (click)="logout()"> Logout </a>
|
||||||
<hr class="navbar-divider" />
|
<hr class="navbar-divider" />
|
||||||
<a href="https://github.com/rogerfar/rdt-client" target="_blank" class="navbar-item">Version 2.0.86</a>
|
<a href="https://github.com/rogerfar/rdt-client" target="_blank" class="navbar-item">Version 2.0.90</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,11 @@ export class NavbarComponent implements OnInit {
|
||||||
public profile: Profile;
|
public profile: Profile;
|
||||||
public providerLink: string;
|
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 {
|
ngOnInit(): void {
|
||||||
this.settingsService.getProfile().subscribe((result) => {
|
this.settingsService.getProfile().subscribe((result) => {
|
||||||
|
|
@ -31,6 +35,9 @@ export class NavbarComponent implements OnInit {
|
||||||
case 'Premiumize':
|
case 'Premiumize':
|
||||||
this.providerLink = 'https://www.premiumize.me/';
|
this.providerLink = 'https://www.premiumize.me/';
|
||||||
break;
|
break;
|
||||||
|
case 'TorBox':
|
||||||
|
this.providerLink = 'https://torbox.app/';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +47,7 @@ export class NavbarComponent implements OnInit {
|
||||||
() => {
|
() => {
|
||||||
this.router.navigate(['/login']);
|
this.router.navigate(['/login']);
|
||||||
},
|
},
|
||||||
(err) => {}
|
(err) => {},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
<div class="notification is-primary">
|
<div class="notification is-primary">
|
||||||
To be able to use the Real-Debrid Client you need a premium subscription to download torrents.
|
To be able to use the Real-Debrid Client you need a premium subscription to download torrents.
|
||||||
<br /><br />
|
<br /><br />
|
||||||
Not premium yet? You have the choice of 2 providers:
|
Not premium yet? You have the choice of 4 providers:
|
||||||
<br />
|
<br />
|
||||||
<a href="https://real-debrid.com/?id=1348683" target="_blank" rel="noopener"
|
<a href="https://real-debrid.com/?id=1348683" target="_blank" rel="noopener"
|
||||||
>Use this link to sign up to Real-Debrid.</a
|
>Use this link to sign up to Real-Debrid.</a
|
||||||
|
|
@ -55,6 +55,8 @@
|
||||||
<a href="https://www.premiumize.me/" target="_blank" rel="noopener"
|
<a href="https://www.premiumize.me/" target="_blank" rel="noopener"
|
||||||
>Use this link to sign up to Premiumize.</a
|
>Use this link to sign up to Premiumize.</a
|
||||||
>
|
>
|
||||||
|
<a href="https://torbox.app/" target="_blank" rel="noopener"
|
||||||
|
>Use this link to sign up to TorBox.</a>
|
||||||
<br />
|
<br />
|
||||||
<small>(Referal links)</small>
|
<small>(Referal links)</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,6 +67,7 @@
|
||||||
<option [ngValue]="0">Real-Debrid</option>
|
<option [ngValue]="0">Real-Debrid</option>
|
||||||
<option [ngValue]="1">AllDebrid</option>
|
<option [ngValue]="1">AllDebrid</option>
|
||||||
<option [ngValue]="2">Premiumize</option>
|
<option [ngValue]="2">Premiumize</option>
|
||||||
|
<option [ngValue]="3">TorBox</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -91,6 +94,10 @@
|
||||||
>https://www.premiumize.me/account</a
|
>https://www.premiumize.me/account</a
|
||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
|
<p class="help" *ngIf="provider === 3">
|
||||||
|
You can find your API key here:
|
||||||
|
<a href="https://torbox.app/settings" target="_blank" rel="noopener">https://torbox.app/settings</a>.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "rdt-client",
|
"name": "rdt-client",
|
||||||
"version": "2.0.86",
|
"version": "2.0.90",
|
||||||
"description": "This is a web interface to manage your torrents on Real-Debrid.",
|
"description": "This is a web interface to manage your torrents on Real-Debrid.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,12 @@ public class TorrentData(DataContext dataContext)
|
||||||
|
|
||||||
public async Task<Torrent?> GetByHash(String hash)
|
public async Task<Torrent?> 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
|
var dbTorrent = await dataContext.Torrents
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(m => m.Downloads)
|
.Include(m => m.Downloads)
|
||||||
.FirstOrDefaultAsync(m => m.Hash.ToLower() == hash.ToLower());
|
.FirstOrDefaultAsync(m => m.Hash == hash);
|
||||||
#pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons
|
|
||||||
|
|
||||||
if (dbTorrent == null)
|
if (dbTorrent == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,8 @@ public enum Provider
|
||||||
AllDebrid,
|
AllDebrid,
|
||||||
|
|
||||||
[Description("Premiumize")]
|
[Description("Premiumize")]
|
||||||
Premiumize
|
Premiumize,
|
||||||
|
|
||||||
|
[Description("TorBox")]
|
||||||
|
TorBox
|
||||||
}
|
}
|
||||||
|
|
@ -155,10 +155,11 @@ http://127.0.0.1:6800/jsonrpc.")]
|
||||||
public class DbSettingsProvider
|
public class DbSettingsProvider
|
||||||
{
|
{
|
||||||
[DisplayName("Provider")]
|
[DisplayName("Provider")]
|
||||||
[Description(@"The following 3 providers are supported:
|
[Description(@"The following 4 providers are supported:
|
||||||
<a href=""https://real-debrid.com/?id=1348683"" target=""_blank"" rel=""noopener"">https://real-debrid.com</a>
|
<a href=""https://real-debrid.com/?id=1348683"" target=""_blank"" rel=""noopener"">https://real-debrid.com</a>
|
||||||
<a href=""https://alldebrid.com/?uid=2v91l&lang=en"" target=""_blank"" rel=""noopener"">https://alldebrid.com</a>
|
<a href=""https://alldebrid.com/?uid=2v91l&lang=en"" target=""_blank"" rel=""noopener"">https://alldebrid.com</a>
|
||||||
<a href=""https://www.premiumize.me/"" target=""_blank"" rel=""noopener"">https://www.premiumize.me/</a>
|
<a href=""https://www.premiumize.me/"" target=""_blank"" rel=""noopener"">https://www.premiumize.me/</a>
|
||||||
|
<a href=""https://torbox.app/"" target=""_blank"" rel=""noopener"">https://torbox.app/</a>
|
||||||
At this point only 1 provider can be used at the time.")]
|
At this point only 1 provider can be used at the time.")]
|
||||||
public Provider Provider { get; set; } = Provider.RealDebrid;
|
public Provider Provider { get; set; } = Provider.RealDebrid;
|
||||||
|
|
||||||
|
|
@ -168,7 +169,9 @@ At this point only 1 provider can be used at the time.")]
|
||||||
or
|
or
|
||||||
<a href=""""https://alldebrid.com/apikeys/"""" target=""""_blank"""" rel=""""noopener"""">https://alldebrid.com/apikeys/</a>
|
<a href=""""https://alldebrid.com/apikeys/"""" target=""""_blank"""" rel=""""noopener"""">https://alldebrid.com/apikeys/</a>
|
||||||
or
|
or
|
||||||
<a href=""https://www.premiumize.me/account/"" target=""_blank"" rel=""noopener"">https://www.premiumize.me/account/</a>")]
|
<a href=""https://www.premiumize.me/account/"" target=""_blank"" rel=""noopener"">https://www.premiumize.me/account/</a>
|
||||||
|
or
|
||||||
|
<a href=""""https://torbox.app/settings/"""" target=""""_blank"""" rel=""""noopener"""">https://torbox.app/settings/</a>")]
|
||||||
public String ApiKey { get; set; } = "";
|
public String ApiKey { get; set; } = "";
|
||||||
|
|
||||||
[DisplayName("Automatically import and process torrents added to provider")]
|
[DisplayName("Automatically import and process torrents added to provider")]
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ public static class DiConfig
|
||||||
services.AddScoped<RemoteService>();
|
services.AddScoped<RemoteService>();
|
||||||
services.AddScoped<RealDebridTorrentClient>();
|
services.AddScoped<RealDebridTorrentClient>();
|
||||||
services.AddScoped<Settings>();
|
services.AddScoped<Settings>();
|
||||||
|
services.AddScoped<TorBoxTorrentClient>();
|
||||||
services.AddScoped<Torrents>();
|
services.AddScoped<Torrents>();
|
||||||
services.AddScoped<TorrentRunner>();
|
services.AddScoped<TorrentRunner>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|
@ -11,17 +11,18 @@
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="AllDebrid.NET" Version="1.0.12" />
|
<PackageReference Include="AllDebrid.NET" Version="1.0.12" />
|
||||||
<PackageReference Include="Aria2.NET" Version="1.0.5" />
|
<PackageReference Include="Aria2.NET" Version="1.0.5" />
|
||||||
<PackageReference Include="Downloader" Version="3.1.2" />
|
<PackageReference Include="Downloader" Version="3.3.1" />
|
||||||
<PackageReference Include="Downloader.NET" Version="1.0.12" />
|
<PackageReference Include="Downloader.NET" Version="1.0.12" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.8.0" />
|
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.0.0" />
|
||||||
<PackageReference Include="MonoTorrent" Version="3.0.2" />
|
<PackageReference Include="MonoTorrent" Version="3.0.2" />
|
||||||
<PackageReference Include="Polly" Version="8.4.1" />
|
<PackageReference Include="Polly" Version="8.5.0" />
|
||||||
<PackageReference Include="Premiumize.NET" Version="1.0.4" />
|
<PackageReference Include="Premiumize.NET" Version="1.0.4" />
|
||||||
<PackageReference Include="RD.NET" Version="2.1.6" />
|
<PackageReference Include="RD.NET" Version="2.1.6" />
|
||||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
||||||
|
<PackageReference Include="TorBox.NET" Version="1.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ public class InternalDownloader : IDownloader
|
||||||
|
|
||||||
_uri = uri;
|
_uri = uri;
|
||||||
_filePath = filePath;
|
_filePath = filePath;
|
||||||
|
|
||||||
_downloadConfiguration = new();
|
_downloadConfiguration = new();
|
||||||
|
|
||||||
SetSettings();
|
SetSettings();
|
||||||
|
|
@ -131,14 +131,14 @@ public class InternalDownloader : IDownloader
|
||||||
{
|
{
|
||||||
settingDownloadParallelCount = 1;
|
settingDownloadParallelCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settingDownloadMaxSpeed = Settings.Get.DownloadClient.MaxSpeed;
|
var settingDownloadMaxSpeed = Settings.Get.DownloadClient.MaxSpeed;
|
||||||
|
|
||||||
if (settingDownloadMaxSpeed <= 0)
|
if (settingDownloadMaxSpeed <= 0)
|
||||||
{
|
{
|
||||||
settingDownloadMaxSpeed = 0;
|
settingDownloadMaxSpeed = 0;
|
||||||
}
|
}
|
||||||
|
settingDownloadMaxSpeed /= Math.Max(TorrentRunner.ActiveDownloadClients.Count, 1);
|
||||||
settingDownloadMaxSpeed = settingDownloadMaxSpeed * 1024 * 1024;
|
settingDownloadMaxSpeed = settingDownloadMaxSpeed * 1024 * 1024;
|
||||||
|
|
||||||
var settingDownloadTimeout = Settings.Get.DownloadClient.Timeout;
|
var settingDownloadTimeout = Settings.Get.DownloadClient.Timeout;
|
||||||
|
|
|
||||||
|
|
@ -118,23 +118,9 @@ public class AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IHtt
|
||||||
return resultId;
|
return resultId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
|
public Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
|
||||||
{
|
{
|
||||||
var isAvailable = await GetClient().Magnet.InstantAvailabilityAsync(hash);
|
return Task.FromResult<IList<TorrentClientAvailableFile>>([]);
|
||||||
|
|
||||||
if (isAvailable)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
[
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
Filename = "All files",
|
|
||||||
Filesize = 0
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SelectFiles(Torrent torrent)
|
public Task SelectFiles(Torrent torrent)
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,7 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
|
||||||
|
|
||||||
public Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
|
public Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
|
||||||
{
|
{
|
||||||
var result = new List<TorrentClientAvailableFile>();
|
return Task.FromResult<IList<TorrentClientAvailableFile>>([]);
|
||||||
return Task.FromResult<IList<TorrentClientAvailableFile>>(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SelectFiles(Torrent torrent)
|
public Task SelectFiles(Torrent torrent)
|
||||||
|
|
|
||||||
|
|
@ -137,49 +137,27 @@ public class RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IH
|
||||||
return result.Id;
|
return result.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
|
public Task<IList<TorrentClientAvailableFile>> GetAvailableFiles(String hash)
|
||||||
{
|
{
|
||||||
var result = await GetClient().Torrents.GetAvailableFiles(hash);
|
return Task.FromResult<IList<TorrentClientAvailableFile>>([]);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectFiles(Data.Models.Data.Torrent torrent)
|
public async Task SelectFiles(Data.Models.Data.Torrent torrent)
|
||||||
{
|
{
|
||||||
var files = torrent.Files;
|
IList<TorrentClientFile> files;
|
||||||
|
|
||||||
Log("Seleting files", torrent);
|
Log("Seleting files", torrent);
|
||||||
|
|
||||||
if (torrent.DownloadAction == TorrentDownloadAction.DownloadAvailableFiles)
|
if (torrent.DownloadAction == TorrentDownloadAction.DownloadManual)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Log("Selecting manual selected files", torrent);
|
Log("Selecting manual selected files", torrent);
|
||||||
files = torrent.Files.Where(m => torrent.ManualFiles.Any(f => m.Path.EndsWith(f))).ToList();
|
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);
|
Log($"Selecting {files.Count}/{torrent.Files.Count} files", torrent);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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<TorBoxTorrentClient> 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<IList<TorrentClientTorrent>> GetTorrents()
|
||||||
|
{
|
||||||
|
var torrents = new List<TorrentInfoResult>();
|
||||||
|
|
||||||
|
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<TorrentClientUser> 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<String> 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<String> 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<IList<TorrentClientAvailableFile>> 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<String> 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<Data.Models.Data.Torrent> 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<IList<String>?> GetDownloadLinks(Data.Models.Data.Torrent torrent)
|
||||||
|
{
|
||||||
|
var files = new List<String>();
|
||||||
|
|
||||||
|
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<TorrentClientTorrent> GetInfo(String torrentHash)
|
||||||
|
{
|
||||||
|
var result = await GetClient().Torrents.GetHashInfoAsync(torrentHash, skipCache: true);
|
||||||
|
|
||||||
|
return Map(result!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,8 @@ public class Torrents(
|
||||||
Downloads downloads,
|
Downloads downloads,
|
||||||
AllDebridTorrentClient allDebridTorrentClient,
|
AllDebridTorrentClient allDebridTorrentClient,
|
||||||
PremiumizeTorrentClient premiumizeTorrentClient,
|
PremiumizeTorrentClient premiumizeTorrentClient,
|
||||||
RealDebridTorrentClient realDebridTorrentClient)
|
RealDebridTorrentClient realDebridTorrentClient,
|
||||||
|
TorBoxTorrentClient torBoxTorrentClient)
|
||||||
{
|
{
|
||||||
private static readonly SemaphoreSlim RealDebridUpdateLock = new(1, 1);
|
private static readonly SemaphoreSlim RealDebridUpdateLock = new(1, 1);
|
||||||
|
|
||||||
|
|
@ -40,6 +41,7 @@ public class Torrents(
|
||||||
Provider.Premiumize => premiumizeTorrentClient,
|
Provider.Premiumize => premiumizeTorrentClient,
|
||||||
Provider.RealDebrid => realDebridTorrentClient,
|
Provider.RealDebrid => realDebridTorrentClient,
|
||||||
Provider.AllDebrid => allDebridTorrentClient,
|
Provider.AllDebrid => allDebridTorrentClient,
|
||||||
|
Provider.TorBox => torBoxTorrentClient,
|
||||||
_ => throw new("Invalid Provider")
|
_ => throw new("Invalid Provider")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using RdtClient.Data.Enums;
|
||||||
using RdtClient.Data.Models.QBittorrent;
|
using RdtClient.Data.Models.QBittorrent;
|
||||||
using RdtClient.Service.Services;
|
using RdtClient.Service.Services;
|
||||||
|
|
||||||
|
|
@ -21,6 +22,11 @@ public class QBittorrentController(ILogger<QBittorrentController> logger, QBitto
|
||||||
{
|
{
|
||||||
logger.LogDebug($"Auth login");
|
logger.LogDebug($"Auth login");
|
||||||
|
|
||||||
|
if (Settings.Get.General.AuthenticationType == AuthenticationType.None)
|
||||||
|
{
|
||||||
|
return Ok("Ok.");
|
||||||
|
}
|
||||||
|
|
||||||
if (String.IsNullOrWhiteSpace(request.UserName) || String.IsNullOrEmpty(request.Password))
|
if (String.IsNullOrWhiteSpace(request.UserName) || String.IsNullOrEmpty(request.Password))
|
||||||
{
|
{
|
||||||
return Ok("Fails.");
|
return Ok("Fails.");
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Hosting.WindowsServices;
|
using Microsoft.Extensions.Hosting.WindowsServices;
|
||||||
|
|
@ -136,8 +135,6 @@ builder.Host.UseWindowsService();
|
||||||
RdtClient.Data.DiConfig.Config(builder.Services, appSettings);
|
RdtClient.Data.DiConfig.Config(builder.Services, appSettings);
|
||||||
builder.Services.RegisterRdtServices();
|
builder.Services.RegisterRdtServices();
|
||||||
|
|
||||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Build the app
|
// Build the app
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<UserSecretsId>94c24cba-f03f-4453-a671-3640b517c573</UserSecretsId>
|
<UserSecretsId>94c24cba-f03f-4453-a671-3640b517c573</UserSecretsId>
|
||||||
<Version>2.0.86</Version>
|
<Version>2.0.90</Version>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|
@ -29,17 +29,17 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" />
|
||||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -199,10 +199,12 @@
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EPsiFeatures_002EVisualStudio_002EBackend_002EDaemon_002EHighlightingSettingsMigrator/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Housekeeping/VsHighlighting/SuppressVsSquiggles/@EntryValue">True</s:Boolean>
|
||||||
</wpf:ResourceDictionary>
|
</wpf:ResourceDictionary>
|
||||||
Loading…
Reference in a new issue