Switched out RDNET Dll with Nuget package, update Angular. Rewrote Download handler and removed references to Hangfire.
This commit is contained in:
parent
76b8513d6b
commit
bfba5c99fb
39 changed files with 1183 additions and 856 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -33,11 +33,7 @@ client/e2e/*.map
|
|||
# .NET Core build folders
|
||||
server/**/bin
|
||||
server/**/obj
|
||||
server/*.user
|
||||
|
||||
# Common node modules locations
|
||||
server/wwwroot/
|
||||
server/.vs
|
||||
server/**/*.user
|
||||
server/**/.vs
|
||||
server/RdtClient.Web/wwwroot/
|
||||
server/RdtClient.Web/Properties/PublishProfiles/FolderProfile.pubxml.user
|
||||
*.user
|
||||
|
||||
|
|
|
|||
1183
client/package-lock.json
generated
1183
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -11,33 +11,34 @@
|
|||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.0",
|
||||
"@angular/common": "~9.1.0",
|
||||
"@angular/compiler": "~9.1.0",
|
||||
"@angular/core": "~9.1.0",
|
||||
"@angular/forms": "~9.1.0",
|
||||
"@angular/platform-browser": "~9.1.0",
|
||||
"@angular/platform-browser-dynamic": "~9.1.0",
|
||||
"@angular/router": "~9.1.0",
|
||||
"@angular/animations": "~9.1.1",
|
||||
"@angular/common": "~9.1.1",
|
||||
"@angular/compiler": "~9.1.1",
|
||||
"@angular/core": "~9.1.1",
|
||||
"@angular/forms": "~9.1.1",
|
||||
"@angular/platform-browser": "~9.1.1",
|
||||
"@angular/platform-browser-dynamic": "~9.1.1",
|
||||
"@angular/router": "~9.1.1",
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
"bulma": "^0.8.1",
|
||||
"curray": "^1.0.7",
|
||||
"ngx-filesize": "^2.0.13",
|
||||
"rxjs": "~6.5.5",
|
||||
"tslib": "^1.11.1",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.0",
|
||||
"@angular/cli": "~9.1.0",
|
||||
"@angular/compiler-cli": "~9.1.0",
|
||||
"@angular/language-service": "~9.1.0",
|
||||
"@types/node": "^13.11.0",
|
||||
"@angular-devkit/build-angular": "~0.901.1",
|
||||
"@angular/cli": "~9.1.1",
|
||||
"@angular/compiler-cli": "~9.1.1",
|
||||
"@angular/language-service": "~9.1.1",
|
||||
"@types/node": "^13.11.1",
|
||||
"@types/jasmine": "~3.5.10",
|
||||
"@types/jasminewd2": "~2.0.8",
|
||||
"codelyzer": "^5.2.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-spec-reporter": "~5.0.1",
|
||||
"karma": "~4.4.1",
|
||||
"karma": "~5.0.1",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.1",
|
||||
"karma-jasmine": "~3.1.1",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ import { TorrentStatusPipe } from './torrent-status.pipe';
|
|||
import { FileStatusPipe } from './file-status.pipe';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { AuthInterceptor } from './auth.interceptor';
|
||||
import { curray } from 'curray';
|
||||
|
||||
curray();
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@ export class FileStatusPipe implements PipeTransform {
|
|||
}
|
||||
|
||||
if (value.download.status === DownloadStatus.Downloading) {
|
||||
return `${value.download.progress}%`;
|
||||
const progress = (
|
||||
(value.download.bytesDownloaded / value.download.bytesSize) *
|
||||
100
|
||||
).toFixed(2);
|
||||
return `${progress || 0}%`;
|
||||
}
|
||||
|
||||
if (value.download.status === DownloadStatus.Finished) {
|
||||
|
|
|
|||
|
|
@ -9,13 +9,16 @@ export class Download {
|
|||
|
||||
public status: DownloadStatus;
|
||||
|
||||
public progress: number;
|
||||
public bytesDownloaded: number;
|
||||
|
||||
public bytesSize: number;
|
||||
|
||||
public speed: number;
|
||||
}
|
||||
|
||||
export enum DownloadStatus {
|
||||
PendingDownload = 0,
|
||||
Downloading,
|
||||
Finished,
|
||||
Downloading = 1,
|
||||
Unpacking = 2,
|
||||
Finished = 3,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ export class Torrent {
|
|||
|
||||
files: TorrentFile[];
|
||||
downloads: Download[];
|
||||
activeDownload: Download;
|
||||
}
|
||||
|
||||
export class TorrentFile {
|
||||
|
|
@ -33,9 +32,10 @@ export class TorrentFile {
|
|||
|
||||
export enum TorrentStatus {
|
||||
RealDebrid = 0,
|
||||
WaitingForDownload,
|
||||
Downloading,
|
||||
Finished,
|
||||
WaitingForDownload = 1,
|
||||
DownloadQueued = 2,
|
||||
Downloading = 3,
|
||||
Finished = 4,
|
||||
|
||||
Error = 99,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,7 @@
|
|||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</span>
|
||||
<span class="icon loading-icon" *ngIf="loading">
|
||||
<i class="fas fa-spinner fa-pulse"></i>
|
||||
</span>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Torrent, TorrentStatus } from './models/torrent.model';
|
||||
import { FileSizePipe } from 'ngx-filesize';
|
||||
import { DownloadStatus } from './models/download.model';
|
||||
|
||||
@Pipe({
|
||||
name: 'status',
|
||||
|
|
@ -9,23 +10,59 @@ export class TorrentStatusPipe implements PipeTransform {
|
|||
constructor(private pipe: FileSizePipe) {}
|
||||
|
||||
transform(torrent: Torrent): string {
|
||||
switch (torrent.status) {
|
||||
case TorrentStatus.RealDebrid: {
|
||||
const speed = this.pipe.transform(torrent.rdSpeed, 'filesize');
|
||||
return `Downloading from RD (${torrent.rdProgress}% - ${speed}/s)`;
|
||||
if (torrent.downloads && torrent.downloads.length > 0) {
|
||||
const allFinished = torrent.downloads.all(
|
||||
(m) => m.status === DownloadStatus.Finished
|
||||
);
|
||||
if (allFinished) {
|
||||
return 'Finished';
|
||||
}
|
||||
|
||||
const downloading = torrent.downloads.where(
|
||||
(m) => m.status === DownloadStatus.Downloading
|
||||
);
|
||||
const unpacking = torrent.downloads.where(
|
||||
(m) => m.status === DownloadStatus.Unpacking
|
||||
);
|
||||
|
||||
if (downloading.length > 0) {
|
||||
const allBytesDownloaded = torrent.downloads.sum(
|
||||
(m) => m.bytesDownloaded
|
||||
);
|
||||
const allBytesSize = torrent.downloads.sum((m) => m.bytesSize);
|
||||
|
||||
if (allBytesSize > 0) {
|
||||
const progress = ((allBytesDownloaded / allBytesSize) * 100).toFixed(
|
||||
2
|
||||
);
|
||||
|
||||
const allSpeeds =
|
||||
downloading.sum((m) => m.speed) / downloading.length;
|
||||
const speed = this.pipe.transform(allSpeeds, 'filesize');
|
||||
|
||||
return `Downloading (${progress || 0}% - ${speed}/s)`;
|
||||
}
|
||||
|
||||
return `Preparing download`;
|
||||
}
|
||||
|
||||
if (unpacking.length > 0) {
|
||||
return `Unpacking`;
|
||||
}
|
||||
|
||||
return 'Pending download';
|
||||
}
|
||||
|
||||
switch (torrent.status) {
|
||||
case TorrentStatus.RealDebrid:
|
||||
const speed = this.pipe.transform(torrent.rdSpeed, 'filesize');
|
||||
return `Torrent downloading (${torrent.rdProgress}% - ${speed}/s)`;
|
||||
case TorrentStatus.WaitingForDownload:
|
||||
return `Waiting to download`;
|
||||
case TorrentStatus.Downloading: {
|
||||
if (torrent.activeDownload != null) {
|
||||
const speed = this.pipe.transform(
|
||||
torrent.activeDownload.speed,
|
||||
'filesize'
|
||||
);
|
||||
return `Downloading (${torrent.activeDownload.progress}% - ${speed}/s)`;
|
||||
}
|
||||
case TorrentStatus.DownloadQueued:
|
||||
return `Download queued`;
|
||||
case TorrentStatus.Downloading:
|
||||
return `Downloading`;
|
||||
}
|
||||
case TorrentStatus.Finished:
|
||||
return `Finished`;
|
||||
case TorrentStatus.Error:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
<div class="notification is-danger is-light" *ngIf="error && error.length > 0">
|
||||
An error has occured: {{ error }}<br />
|
||||
Please refresh the screen after fixing this error.
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<colgroup>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { DownloadStatus } from '../models/download.model';
|
|||
})
|
||||
export class TorrentTableComponent implements OnInit, OnDestroy {
|
||||
public torrents: Torrent[] = [];
|
||||
|
||||
public error: string;
|
||||
public showFiles: { [key: string]: boolean } = {};
|
||||
|
||||
private timer: any;
|
||||
|
|
@ -25,18 +25,15 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.timer = setInterval(() => {
|
||||
this.torrentService.getList().subscribe((result) => {
|
||||
this.torrents = result;
|
||||
|
||||
this.torrents.forEach((torrent) => {
|
||||
const activeDownloads = torrent.downloads.filter(
|
||||
(m) => m.status === DownloadStatus.Downloading
|
||||
);
|
||||
if (activeDownloads.length > 0) {
|
||||
torrent.activeDownload = activeDownloads[0];
|
||||
}
|
||||
});
|
||||
});
|
||||
this.torrentService.getList().subscribe(
|
||||
(result) => {
|
||||
this.torrents = result;
|
||||
},
|
||||
(err) => {
|
||||
this.error = err.error;
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
"parameters",
|
||||
"statements"
|
||||
]
|
||||
"options": ["parameters", "statements"]
|
||||
},
|
||||
"array-type": false,
|
||||
"arrow-return-shorthand": true,
|
||||
|
|
@ -16,34 +13,16 @@
|
|||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
],
|
||||
"directive-selector": [true, "attribute", "app", "camelCase"],
|
||||
"component-selector": [true, "element", "app", "kebab-case"],
|
||||
"eofline": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"import-blacklist": [true, "rxjs/Rx"],
|
||||
"import-spacing": true,
|
||||
"indent": {
|
||||
"options": [
|
||||
"spaces"
|
||||
]
|
||||
"options": ["spaces"]
|
||||
},
|
||||
"max-classes-per-file": false,
|
||||
"max-line-length": [
|
||||
true,
|
||||
140
|
||||
],
|
||||
"max-line-length": [true, 140],
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
|
|
@ -55,35 +34,17 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
|
||||
"no-empty": false,
|
||||
"no-inferrable-types": [
|
||||
true,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-inferrable-types": [true, "ignore-params"],
|
||||
"no-non-null-assertion": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"as-needed"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"object-literal-key-quotes": [true, "as-needed"],
|
||||
"quotemark": [true, "single"],
|
||||
"semicolon": {
|
||||
"options": [
|
||||
"always"
|
||||
]
|
||||
"options": ["always"]
|
||||
},
|
||||
"space-before-function-paren": {
|
||||
"options": {
|
||||
|
|
@ -113,11 +74,7 @@
|
|||
]
|
||||
},
|
||||
"variable-name": {
|
||||
"options": [
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-pascal-case"
|
||||
]
|
||||
"options": ["ban-keywords", "check-format", "allow-pascal-case"]
|
||||
},
|
||||
"whitespace": {
|
||||
"options": [
|
||||
|
|
@ -142,7 +99,5 @@
|
|||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
}
|
||||
"rulesDirectory": ["codelyzer"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace RdtClient.Data.Data
|
|||
public interface IDownloadData
|
||||
{
|
||||
Task<IList<Download>> Get();
|
||||
Task<IList<Download>> GetForTorrent(Guid torrentId);
|
||||
Task<Download> Add(Guid torrentId, String link);
|
||||
Task UpdateStatus(Guid downloadId, DownloadStatus status);
|
||||
Task DeleteForTorrent(Guid torrentId);
|
||||
|
|
@ -33,6 +34,14 @@ namespace RdtClient.Data.Data
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<Download>> GetForTorrent(Guid torrentId)
|
||||
{
|
||||
return await _dataContext.Downloads
|
||||
.AsNoTracking()
|
||||
.Where(m => m.TorrentId == torrentId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Download> Add(Guid torrentId, String link)
|
||||
{
|
||||
var download = new Download
|
||||
|
|
|
|||
|
|
@ -101,8 +101,6 @@ namespace RdtClient.Data.Data
|
|||
{
|
||||
var dbTorrent = await _dataContext.Torrents.FirstOrDefaultAsync(m => m.TorrentId == torrent.TorrentId);
|
||||
|
||||
dbTorrent.Status = torrent.Status;
|
||||
|
||||
dbTorrent.RdName = torrent.RdName;
|
||||
dbTorrent.RdSize = torrent.RdSize;
|
||||
dbTorrent.RdHost = torrent.RdHost;
|
||||
|
|
@ -114,6 +112,11 @@ namespace RdtClient.Data.Data
|
|||
dbTorrent.RdSpeed = torrent.RdSpeed;
|
||||
dbTorrent.RdSeeders = torrent.RdSeeders;
|
||||
|
||||
if (torrent.Files != null)
|
||||
{
|
||||
dbTorrent.RdFiles = torrent.RdFiles;
|
||||
}
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
public enum TorrentStatus
|
||||
{
|
||||
RealDebrid = 0,
|
||||
WaitingForDownload,
|
||||
Downloading,
|
||||
Finished,
|
||||
WaitingForDownload = 1,
|
||||
DownloadQueued = 2,
|
||||
Downloading = 3,
|
||||
Finished = 4,
|
||||
|
||||
Error = 99
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using RdtClient.Data.Data;
|
|||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20200407174750_Initial")]
|
||||
[Migration("20200408224831_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
|
|
@ -277,6 +277,12 @@ namespace RdtClient.Data.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("AutoDelete")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("AutoDownload")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
|
@ -66,6 +66,8 @@ namespace RdtClient.Data.Migrations
|
|||
TorrentId = table.Column<Guid>(nullable: false),
|
||||
Hash = table.Column<string>(nullable: true),
|
||||
Category = table.Column<string>(nullable: true),
|
||||
AutoDownload = table.Column<bool>(nullable: false),
|
||||
AutoDelete = table.Column<bool>(nullable: false),
|
||||
Status = table.Column<int>(nullable: false),
|
||||
RdId = table.Column<string>(nullable: true),
|
||||
RdName = table.Column<string>(nullable: true),
|
||||
|
|
@ -275,6 +275,12 @@ namespace RdtClient.Data.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("AutoDelete")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("AutoDownload")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
|
|
|||
|
|
@ -22,15 +22,12 @@ namespace RdtClient.Data.Models.Data
|
|||
public Torrent Torrent { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Int32 Progress { get; set; }
|
||||
public Int64 BytesSize { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Int64 BytesDownloaded { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Int64 Speed { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateTime NextUpdate { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Int64 BytesLastUpdate { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Newtonsoft.Json;
|
||||
using RDNET.Models;
|
||||
using RDNET;
|
||||
using RdtClient.Data.Enums;
|
||||
|
||||
namespace RdtClient.Data.Models.Data
|
||||
|
|
@ -17,6 +17,10 @@ namespace RdtClient.Data.Models.Data
|
|||
|
||||
public String Category { get; set; }
|
||||
|
||||
public Boolean AutoDownload { get; set; }
|
||||
|
||||
public Boolean AutoDelete { get; set; }
|
||||
|
||||
public TorrentStatus Status { get; set; }
|
||||
|
||||
[InverseProperty("Torrent")]
|
||||
|
|
|
|||
|
|
@ -4,31 +4,6 @@
|
|||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Migrations\20200402172935_AspNetIdentity.cs" />
|
||||
<Compile Remove="Migrations\20200402172935_AspNetIdentity.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200402173119_Torrents.cs" />
|
||||
<Compile Remove="Migrations\20200402173119_Torrents.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200402190746_Torrents.cs" />
|
||||
<Compile Remove="Migrations\20200402190746_Torrents.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200402194059_Torrents.cs" />
|
||||
<Compile Remove="Migrations\20200402194059_Torrents.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200402212417_Torrents.cs" />
|
||||
<Compile Remove="Migrations\20200402212417_Torrents.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200403143200_Torrent.cs" />
|
||||
<Compile Remove="Migrations\20200403143200_Torrent.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200403143240_Settings.cs" />
|
||||
<Compile Remove="Migrations\20200403143240_Settings.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200403145651_Settings.cs" />
|
||||
<Compile Remove="Migrations\20200403145651_Settings.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200403164143_Downloads.cs" />
|
||||
<Compile Remove="Migrations\20200403164143_Downloads.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200403174830_Initia.cs" />
|
||||
<Compile Remove="Migrations\20200403174830_Initia.Designer.cs" />
|
||||
<Compile Remove="Migrations\20200403174846_Initial.cs" />
|
||||
<Compile Remove="Migrations\20200403174846_Initial.Designer.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="3.1.3" />
|
||||
|
|
@ -39,6 +14,7 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="RD.NET" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ namespace RdtClient.Service
|
|||
services.AddScoped<IAuthentication, Authentication>();
|
||||
services.AddScoped<IDownloads, Downloads>();
|
||||
services.AddScoped<IQBittorrent, QBittorrent>();
|
||||
services.AddScoped<IScheduler, Scheduler>();
|
||||
services.AddScoped<ISettings, Settings>();
|
||||
services.AddScoped<ITorrents, Torrents>();
|
||||
|
||||
services.AddHostedService<TaskRunner>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Hangfire.Core" Version="1.7.10" />
|
||||
<PackageReference Include="MonoTorrent" Version="1.0.19" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="RD.NET" Version="1.0.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
@ -16,10 +16,4 @@
|
|||
<ProjectReference Include="..\RdtClient.Data\RdtClient.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="RDNET">
|
||||
<HintPath>..\libs\RDNET.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using SharpCompress.Common;
|
||||
|
|
@ -14,32 +10,37 @@ using SharpCompress.Readers;
|
|||
|
||||
namespace RdtClient.Service.Services
|
||||
{
|
||||
public static class DownloadManager
|
||||
public class DownloadManager
|
||||
{
|
||||
public static readonly Dictionary<Guid, Download> ActiveDownloads = new Dictionary<Guid, Download>();
|
||||
public DownloadStatus? NewStatus { get; set; }
|
||||
public Download Download { get; set; }
|
||||
public Int64 Speed { get; private set; }
|
||||
public Int64 BytesDownloaded { get; private set; }
|
||||
public Int64 BytesSize { get; private set; }
|
||||
|
||||
static DownloadManager()
|
||||
private DateTime _nextUpdate;
|
||||
private Int64 _bytesLastUpdate;
|
||||
|
||||
public DownloadManager()
|
||||
{
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
ServicePointManager.DefaultConnectionLimit = 100;
|
||||
ServicePointManager.MaxServicePointIdleTime = 1000;
|
||||
}
|
||||
|
||||
public static async Task Download(Download download, String destinationFolderPath)
|
||||
private DownloadManager ActiveDownload => TaskRunner.ActiveDownloads[Download.DownloadId];
|
||||
|
||||
public async Task Start(String destinationFolderPath)
|
||||
{
|
||||
await UpdateStatus(download.DownloadId, DownloadStatus.Downloading, TorrentStatus.Downloading);
|
||||
ActiveDownload.NewStatus = DownloadStatus.Downloading;
|
||||
ActiveDownload.BytesDownloaded = 0;
|
||||
ActiveDownload.BytesSize = 0;
|
||||
ActiveDownload.Speed = 0;
|
||||
|
||||
_bytesLastUpdate = 0;
|
||||
_nextUpdate = DateTime.UtcNow.AddSeconds(1);
|
||||
|
||||
if (!ActiveDownloads.TryAdd(download.DownloadId, download))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
download.Progress = 0;
|
||||
download.BytesLastUpdate = 0;
|
||||
download.NextUpdate = DateTime.UtcNow.AddSeconds(1);
|
||||
download.Speed = 0;
|
||||
|
||||
var fileUrl = download.Link;
|
||||
var fileUrl = Download.Link;
|
||||
|
||||
var uri = new Uri(fileUrl);
|
||||
var filePath = Path.Combine(destinationFolderPath, uri.Segments.Last());
|
||||
|
|
@ -77,13 +78,15 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
fileStream.Write(buffer, 0, read);
|
||||
|
||||
ActiveDownloads[download.DownloadId].Progress = (Int32) (fileStream.Length * 100 / responseLength);
|
||||
ActiveDownload.BytesDownloaded = fileStream.Length;
|
||||
ActiveDownload.BytesSize = responseLength;
|
||||
|
||||
if (DateTime.UtcNow > ActiveDownloads[download.DownloadId].NextUpdate)
|
||||
if (DateTime.UtcNow > _nextUpdate)
|
||||
{
|
||||
ActiveDownloads[download.DownloadId].Speed = fileStream.Length - ActiveDownloads[download.DownloadId].BytesLastUpdate;
|
||||
ActiveDownloads[download.DownloadId].NextUpdate = DateTime.UtcNow.AddSeconds(1);
|
||||
ActiveDownloads[download.DownloadId].BytesLastUpdate = fileStream.Length;
|
||||
ActiveDownload.Speed = fileStream.Length - _bytesLastUpdate;
|
||||
|
||||
_nextUpdate = DateTime.UtcNow.AddSeconds(1);
|
||||
_bytesLastUpdate = fileStream.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -93,13 +96,14 @@ namespace RdtClient.Service.Services
|
|||
}
|
||||
}
|
||||
|
||||
ActiveDownloads[download.DownloadId].Speed = 0;
|
||||
ActiveDownload.Speed = 0;
|
||||
ActiveDownload.BytesDownloaded = ActiveDownload.BytesSize;
|
||||
|
||||
try
|
||||
{
|
||||
if (filePath.EndsWith(".rar"))
|
||||
{
|
||||
await UpdateStatus(download.DownloadId, DownloadStatus.Unpacking, TorrentStatus.Downloading);
|
||||
ActiveDownload.NewStatus = DownloadStatus.Unpacking;
|
||||
|
||||
await using (Stream stream = File.OpenRead(filePath))
|
||||
{
|
||||
|
|
@ -141,43 +145,7 @@ namespace RdtClient.Service.Services
|
|||
// ignored
|
||||
}
|
||||
|
||||
await UpdateStatus(download.DownloadId, DownloadStatus.Finished, TorrentStatus.Finished);
|
||||
|
||||
ActiveDownloads.Remove(download.DownloadId);
|
||||
}
|
||||
|
||||
private static async Task UpdateStatus(Guid downloadId, DownloadStatus downloadStatus, TorrentStatus torrentStatus)
|
||||
{
|
||||
await using var context = new DataContext();
|
||||
|
||||
var download = await context.Downloads.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
|
||||
|
||||
download.Status = downloadStatus;
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var torrent = await context.Torrents.FirstOrDefaultAsync(m => m.TorrentId == download.TorrentId);
|
||||
|
||||
if (torrentStatus == TorrentStatus.Finished)
|
||||
{
|
||||
var allDownloads = await context.Downloads.Where(m => m.TorrentId == download.TorrentId)
|
||||
.ToListAsync();
|
||||
|
||||
if (allDownloads.All(m => m.Status == DownloadStatus.Finished))
|
||||
{
|
||||
torrent.Status = TorrentStatus.Finished;
|
||||
}
|
||||
else
|
||||
{
|
||||
torrent.Status = TorrentStatus.Downloading;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
torrent.Status = torrentStatus;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
ActiveDownload.NewStatus = DownloadStatus.Finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ namespace RdtClient.Service.Services
|
|||
public interface IDownloads
|
||||
{
|
||||
Task<IList<Download>> Get();
|
||||
Task<IList<Download>> GetForTorrent(Guid torrentId);
|
||||
Task<Download> Add(Guid torrentId, String link);
|
||||
Task UpdateStatus(Guid downloadId, DownloadStatus status);
|
||||
Task DeleteForTorrent(Guid torrentId);
|
||||
|
|
@ -18,12 +19,10 @@ namespace RdtClient.Service.Services
|
|||
public class Downloads : IDownloads
|
||||
{
|
||||
private readonly IDownloadData _downloadData;
|
||||
private readonly ISettings _settings;
|
||||
|
||||
public Downloads(IDownloadData downloadData, ISettings settings)
|
||||
public Downloads(IDownloadData downloadData)
|
||||
{
|
||||
_downloadData = downloadData;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public async Task<IList<Download>> Get()
|
||||
|
|
@ -31,6 +30,11 @@ namespace RdtClient.Service.Services
|
|||
return await _downloadData.Get();
|
||||
}
|
||||
|
||||
public async Task<IList<Download>> GetForTorrent(Guid torrentId)
|
||||
{
|
||||
return await _downloadData.GetForTorrent(torrentId);
|
||||
}
|
||||
|
||||
public async Task<Download> Add(Guid torrentId, String link)
|
||||
{
|
||||
return await _downloadData.Add(torrentId, link);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace RdtClient.Service.Services
|
|||
Task<IList<TorrentInfo>> TorrentInfo();
|
||||
Task<TorrentProperties> TorrentProperties(String hash);
|
||||
Task TorrentsDelete(String hash, Boolean deleteFiles);
|
||||
Task TorrentsAdd(String url);
|
||||
Task TorrentsAdd(String magnetLink, Boolean autoDownload, Boolean autoDelete);
|
||||
Task TorrentsSetCategory(String hash, String category);
|
||||
Task<IDictionary<String, TorrentCategory>> TorrentsCategories();
|
||||
}
|
||||
|
|
@ -363,9 +363,9 @@ namespace RdtClient.Service.Services
|
|||
}
|
||||
}
|
||||
|
||||
public async Task TorrentsAdd(String url)
|
||||
public async Task TorrentsAdd(String magnetLink, Boolean autoDownload, Boolean autoDelete)
|
||||
{
|
||||
await _torrents.UploadMagnet(url);
|
||||
await _torrents.UploadMagnet(magnetLink, autoDownload, autoDelete);
|
||||
}
|
||||
|
||||
public async Task TorrentsSetCategory(String hash, String category)
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using RdtClient.Data.Enums;
|
||||
|
||||
namespace RdtClient.Service.Services
|
||||
{
|
||||
public interface IScheduler
|
||||
{
|
||||
void Start();
|
||||
Task Process();
|
||||
}
|
||||
|
||||
public class Scheduler : IScheduler
|
||||
{
|
||||
private readonly IDownloads _downloads;
|
||||
private readonly ISettings _settings;
|
||||
private readonly ITorrents _torrents;
|
||||
|
||||
public Scheduler(ITorrents torrents, IDownloads downloads, ISettings settings)
|
||||
{
|
||||
_torrents = torrents;
|
||||
_downloads = downloads;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
RecurringJob.AddOrUpdate(() => Process(), "* * * * *");
|
||||
BackgroundJob.Enqueue(() => Process());
|
||||
}
|
||||
|
||||
[DisableConcurrentExecution(5)]
|
||||
public async Task Process()
|
||||
{
|
||||
await _torrents.Update();
|
||||
|
||||
var downloads = await _downloads.Get();
|
||||
|
||||
downloads = downloads.Where(m => m.Status != DownloadStatus.Finished)
|
||||
.OrderByDescending(m => m.Status)
|
||||
.ThenByDescending(m => m.Added)
|
||||
.ToList();
|
||||
|
||||
var maxDownloads = await _settings.GetNumber("DownloadLimit");
|
||||
var destinationFolderPath = await _settings.GetString("DownloadFolder");
|
||||
|
||||
foreach (var download in downloads)
|
||||
{
|
||||
if (DownloadManager.ActiveDownloads.Count >= maxDownloads)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
download.Torrent = null;
|
||||
BackgroundJob.Enqueue(() => DownloadManager.Download(download, destinationFolderPath));
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
180
server/RdtClient.Service/Services/TaskRunner.cs
Normal file
180
server/RdtClient.Service/Services/TaskRunner.cs
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RdtClient.Data.Enums;
|
||||
|
||||
namespace RdtClient.Service.Services
|
||||
{
|
||||
public class TaskRunner : IHostedService, IDisposable
|
||||
{
|
||||
public static readonly ConcurrentDictionary<Guid, DownloadManager> ActiveDownloads = new ConcurrentDictionary<Guid, DownloadManager>();
|
||||
|
||||
private readonly ILogger<TaskRunner> _logger;
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
|
||||
|
||||
public TaskRunner(ILogger<TaskRunner> logger, IServiceProvider services)
|
||||
{
|
||||
_logger = logger;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Timed Hosted Service running.");
|
||||
|
||||
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Timed Hosted Service is stopping.");
|
||||
|
||||
_timer?.Change(Timeout.Infinite, 0);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
}
|
||||
|
||||
private async void DoWork(Object state)
|
||||
{
|
||||
// Make sure only 1 process enters the lock
|
||||
var obtainLock = await _semaphoreSlim.WaitAsync(100);
|
||||
|
||||
if (!obtainLock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var scope = _services.CreateScope())
|
||||
{
|
||||
var downloads = scope.ServiceProvider.GetRequiredService<IDownloads>();
|
||||
var settings = scope.ServiceProvider.GetRequiredService<ISettings>();
|
||||
var torrents = scope.ServiceProvider.GetRequiredService<ITorrents>();
|
||||
|
||||
var rdKey = await settings.GetString("RealDebridApiKey");
|
||||
|
||||
if (String.IsNullOrWhiteSpace(rdKey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ProcessAutoDownloads(downloads, settings, torrents);
|
||||
await ProcessDownloads(downloads, settings, torrents);
|
||||
await ProcessStatus(downloads, settings, torrents);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphoreSlim.Release(1);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessAutoDownloads(IDownloads downloads, ISettings settings, ITorrents torrents)
|
||||
{
|
||||
await torrents.Update();
|
||||
|
||||
var allTorrents = await torrents.Get();
|
||||
|
||||
allTorrents = allTorrents.Where(m => m.Status == TorrentStatus.WaitingForDownload && m.AutoDownload && m.Downloads.Count == 0)
|
||||
.ToList();
|
||||
|
||||
foreach (var torrent in allTorrents)
|
||||
{
|
||||
await torrents.Download(torrent.TorrentId);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessDownloads(IDownloads downloads, ISettings settings, ITorrents torrents)
|
||||
{
|
||||
await torrents.Update();
|
||||
|
||||
var allDownloads = await downloads.Get();
|
||||
|
||||
allDownloads = allDownloads.Where(m => m.Status != DownloadStatus.Finished)
|
||||
.OrderByDescending(m => m.Status)
|
||||
.ThenByDescending(m => m.Added)
|
||||
.ToList();
|
||||
|
||||
var maxDownloads = await settings.GetNumber("DownloadLimit");
|
||||
var destinationFolderPath = await settings.GetString("DownloadFolder");
|
||||
|
||||
foreach (var download in allDownloads)
|
||||
{
|
||||
if (ActiveDownloads.ContainsKey(download.DownloadId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ActiveDownloads.Count >= maxDownloads)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent circular references
|
||||
download.Torrent.Downloads = null;
|
||||
|
||||
await Task.Factory.StartNew(async delegate
|
||||
{
|
||||
var downloadManager = new DownloadManager();
|
||||
|
||||
if (ActiveDownloads.TryAdd(download.DownloadId, downloadManager))
|
||||
{
|
||||
downloadManager.Download = download;
|
||||
await downloadManager.Start(destinationFolderPath);
|
||||
|
||||
await torrents.UpdateStatus(download.TorrentId, TorrentStatus.Downloading);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessStatus(IDownloads downloads, ISettings settings, ITorrents torrents)
|
||||
{
|
||||
foreach (var (downloadId, download) in ActiveDownloads)
|
||||
{
|
||||
if (download.NewStatus.HasValue)
|
||||
{
|
||||
download.Download.Status = download.NewStatus.Value;
|
||||
download.NewStatus = null;
|
||||
|
||||
await downloads.UpdateStatus(downloadId, download.Download.Status);
|
||||
|
||||
if (download.Download.Status == DownloadStatus.Finished)
|
||||
{
|
||||
ActiveDownloads.TryRemove(downloadId, out _);
|
||||
|
||||
// Check if all downloads are completed and update the torrent
|
||||
var allDownloads = await downloads.GetForTorrent(download.Download.TorrentId);
|
||||
|
||||
if (allDownloads.All(m => m.Status == DownloadStatus.Finished))
|
||||
{
|
||||
await torrents.UpdateStatus(download.Download.TorrentId, TorrentStatus.Finished);
|
||||
|
||||
if (download.Download.Torrent.AutoDelete)
|
||||
{
|
||||
await torrents.Delete(download.Download.TorrentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,10 @@ namespace RdtClient.Service.Services
|
|||
Task<Torrent> GetById(Guid id);
|
||||
Task<Torrent> GetByHash(String hash);
|
||||
Task<IList<Torrent>> Update();
|
||||
Task UpdateStatus(Guid torrentId, TorrentStatus status);
|
||||
Task UpdateCategory(String hash, String category);
|
||||
Task UploadMagnet(String magnetLink);
|
||||
Task UploadFile(Byte[] bytes);
|
||||
Task UploadMagnet(String magnetLink, Boolean autoDownload, Boolean autoDelete);
|
||||
Task UploadFile(Byte[] bytes, Boolean autoDownload, Boolean autoDelete);
|
||||
Task Delete(Guid id);
|
||||
Task Download(Guid id);
|
||||
void Reset();
|
||||
|
|
@ -60,7 +61,7 @@ namespace RdtClient.Service.Services
|
|||
throw new Exception("RealDebrid API Key not set in the settings");
|
||||
}
|
||||
|
||||
_rdtClient = new RdNetClient("X245A4XAIBGVM", null, null, null, null, apiKey);
|
||||
_rdtClient = new RdNetClient("X245A4XAIBGVM", null, null, null, apiKey);
|
||||
}
|
||||
|
||||
return _rdtClient;
|
||||
|
|
@ -82,10 +83,11 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
foreach (var download in torrent.Downloads)
|
||||
{
|
||||
if (DownloadManager.ActiveDownloads.TryGetValue(download.DownloadId, out var activeDownload))
|
||||
if (TaskRunner.ActiveDownloads.TryGetValue(download.DownloadId, out var activeDownload))
|
||||
{
|
||||
download.Speed = activeDownload.Speed;
|
||||
download.Progress = activeDownload.Progress;
|
||||
download.BytesSize = activeDownload.BytesSize;
|
||||
download.BytesDownloaded = activeDownload.BytesDownloaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +101,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
if (torrent != null)
|
||||
{
|
||||
var rdTorrent = await RdNetClient.TorrentInfoAsync(torrent.RdId);
|
||||
var rdTorrent = await RdNetClient.GetTorrentInfoAsync(torrent.RdId);
|
||||
|
||||
await Update(torrent, rdTorrent);
|
||||
}
|
||||
|
|
@ -113,7 +115,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
if (torrent != null)
|
||||
{
|
||||
var rdTorrent = await RdNetClient.TorrentInfoAsync(torrent.RdId);
|
||||
var rdTorrent = await RdNetClient.GetTorrentInfoAsync(torrent.RdId);
|
||||
|
||||
await Update(torrent, rdTorrent);
|
||||
}
|
||||
|
|
@ -133,7 +135,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
try
|
||||
{
|
||||
var rdTorrents = await RdNetClient.TorrentsAsync(0, 100);
|
||||
var rdTorrents = await RdNetClient.GetTorrentsAsync(0, 100);
|
||||
|
||||
foreach (var rdTorrent in rdTorrents)
|
||||
{
|
||||
|
|
@ -169,6 +171,11 @@ namespace RdtClient.Service.Services
|
|||
}
|
||||
}
|
||||
|
||||
public async Task UpdateStatus(Guid torrentId, TorrentStatus status)
|
||||
{
|
||||
await _torrentData.UpdateStatus(torrentId, status);
|
||||
}
|
||||
|
||||
public async Task UpdateCategory(String hash, String category)
|
||||
{
|
||||
var torrent = await _torrentData.GetByHash(hash);
|
||||
|
|
@ -181,22 +188,22 @@ namespace RdtClient.Service.Services
|
|||
await _torrentData.UpdateCategory(torrent.TorrentId, category);
|
||||
}
|
||||
|
||||
public async Task UploadMagnet(String magnetLink)
|
||||
public async Task UploadMagnet(String magnetLink, Boolean autoDownload, Boolean autoDelete)
|
||||
{
|
||||
var magnet = MagnetLink.Parse(magnetLink);
|
||||
|
||||
var rdTorrent = await RdNetClient.TorrentAddMagnet(magnetLink);
|
||||
var rdTorrent = await RdNetClient.AddTorrentMagnetAsync(magnetLink);
|
||||
|
||||
await Add(rdTorrent.Id, magnet.InfoHash.ToHex());
|
||||
await Add(rdTorrent.Id, magnet.InfoHash.ToHex(), autoDownload, autoDelete);
|
||||
}
|
||||
|
||||
public async Task UploadFile(Byte[] bytes)
|
||||
public async Task UploadFile(Byte[] bytes, Boolean autoDownload, Boolean autoDelete)
|
||||
{
|
||||
var torrent = MonoTorrent.Torrent.Load(bytes);
|
||||
|
||||
var rdTorrent = await RdNetClient.TorrentAddFile(bytes);
|
||||
var rdTorrent = await RdNetClient.AddTorrentFileAsync(bytes);
|
||||
|
||||
await Add(rdTorrent.Id, torrent.InfoHash.ToHex());
|
||||
await Add(rdTorrent.Id, torrent.InfoHash.ToHex(), autoDownload, autoDelete);
|
||||
}
|
||||
|
||||
public async Task Delete(Guid id)
|
||||
|
|
@ -207,7 +214,7 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
await _downloads.DeleteForTorrent(torrent.TorrentId);
|
||||
await _torrentData.Delete(id);
|
||||
await RdNetClient.TorrentDelete(torrent.RdId);
|
||||
await RdNetClient.DeleteTorrentAsync(torrent.RdId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +224,9 @@ namespace RdtClient.Service.Services
|
|||
|
||||
await _downloads.DeleteForTorrent(id);
|
||||
|
||||
var rdTorrent = await RdNetClient.TorrentInfoAsync(torrent.RdId);
|
||||
await _torrentData.UpdateStatus(id, TorrentStatus.DownloadQueued);
|
||||
|
||||
var rdTorrent = await RdNetClient.GetTorrentInfoAsync(torrent.RdId);
|
||||
|
||||
foreach (var link in rdTorrent.Links)
|
||||
{
|
||||
|
|
@ -235,7 +244,12 @@ namespace RdtClient.Service.Services
|
|||
|
||||
public async Task<Profile> GetProfile()
|
||||
{
|
||||
var user = await _rdtClient.UserAsync();
|
||||
if (_rdtClient == null)
|
||||
{
|
||||
return new Profile();
|
||||
}
|
||||
|
||||
var user = await _rdtClient.GetUserAsync();
|
||||
|
||||
var profile = new Profile
|
||||
{
|
||||
|
|
@ -246,11 +260,11 @@ namespace RdtClient.Service.Services
|
|||
return profile;
|
||||
}
|
||||
|
||||
private async Task Add(String rdTorrentId, String infoHash)
|
||||
private async Task Add(String rdTorrentId, String infoHash, Boolean autoDownload, Boolean autoDelete)
|
||||
{
|
||||
var newTorrent = await _torrentData.Add(rdTorrentId, infoHash);
|
||||
|
||||
var rdTorrent = await RdNetClient.TorrentInfoAsync(rdTorrentId);
|
||||
var rdTorrent = await RdNetClient.GetTorrentInfoAsync(rdTorrentId);
|
||||
|
||||
if (rdTorrent.Files != null && rdTorrent.Files.Count > 0)
|
||||
{
|
||||
|
|
@ -259,14 +273,14 @@ namespace RdtClient.Service.Services
|
|||
var fileIds = rdTorrent.Files.Select(m => m.Id.ToString())
|
||||
.ToArray();
|
||||
|
||||
await RdNetClient.TorrentSelectFiles(rdTorrentId, fileIds);
|
||||
await RdNetClient.SelectTorrentFilesAsync(rdTorrentId, fileIds);
|
||||
}
|
||||
}
|
||||
|
||||
await Update(newTorrent, rdTorrent);
|
||||
}
|
||||
|
||||
private async Task Update(Torrent torrent, RDNET.Models.Torrent rdTorrent)
|
||||
private async Task Update(Torrent torrent, RDNET.Torrent rdTorrent)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(rdTorrent.Filename))
|
||||
{
|
||||
|
|
@ -300,11 +314,13 @@ namespace RdtClient.Service.Services
|
|||
torrent.RdSpeed = rdTorrent.Speed;
|
||||
torrent.RdSeeders = rdTorrent.Seeders;
|
||||
|
||||
await _torrentData.UpdateRdData(torrent);
|
||||
|
||||
if (torrent.Status == TorrentStatus.RealDebrid)
|
||||
{
|
||||
if (torrent.Status == TorrentStatus.RealDebrid && torrent.RdProgress == 100)
|
||||
{
|
||||
torrent.Status = TorrentStatus.WaitingForDownload;
|
||||
await _torrentData.UpdateStatus(torrent.TorrentId, TorrentStatus.WaitingForDownload);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -317,10 +333,10 @@ namespace RdtClient.Service.Services
|
|||
"dead" => TorrentStatus.Error,
|
||||
_ => TorrentStatus.RealDebrid
|
||||
};
|
||||
|
||||
await _torrentData.UpdateStatus(torrent.TorrentId, torrent.Status);
|
||||
}
|
||||
}
|
||||
|
||||
await _torrentData.UpdateRdData(torrent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -272,7 +272,7 @@ namespace RdtClient.Web.Controllers
|
|||
|
||||
foreach (var url in urls)
|
||||
{
|
||||
await _qBittorrent.TorrentsAdd(url.Trim());
|
||||
await _qBittorrent.TorrentsAdd(url.Trim(), true, true);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace RdtClient.Web.Controllers
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex);
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ namespace RdtClient.Web.Controllers
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex);
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ namespace RdtClient.Web.Controllers
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex);
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,10 @@ namespace RdtClient.Web.Controllers
|
|||
public class TorrentsController : Controller
|
||||
{
|
||||
private readonly ITorrents _torrents;
|
||||
private readonly IScheduler _scheduler;
|
||||
|
||||
public TorrentsController(ITorrents torrents, IScheduler scheduler)
|
||||
public TorrentsController(ITorrents torrents)
|
||||
{
|
||||
_torrents = torrents;
|
||||
_scheduler = scheduler;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -34,7 +32,7 @@ namespace RdtClient.Web.Controllers
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex);
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +53,7 @@ namespace RdtClient.Web.Controllers
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex);
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +76,7 @@ namespace RdtClient.Web.Controllers
|
|||
|
||||
var bytes = memoryStream.ToArray();
|
||||
|
||||
await _torrents.UploadFile(bytes);
|
||||
await _torrents.UploadFile(bytes, false, false);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
|
@ -94,7 +92,7 @@ namespace RdtClient.Web.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
await _torrents.UploadMagnet(request.MagnetLink);
|
||||
await _torrents.UploadMagnet(request.MagnetLink, false, false);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
|
@ -127,7 +125,6 @@ namespace RdtClient.Web.Controllers
|
|||
try
|
||||
{
|
||||
await _torrents.Download(id);
|
||||
await _scheduler.Process();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using Microsoft.AspNetCore;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
|
||||
namespace RdtClient.Web
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,9 +5,6 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.10" />
|
||||
<PackageReference Include="Hangfire.Core" Version="1.7.10" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.3" />
|
||||
|
|
@ -17,7 +14,6 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="3.1.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.0.5" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Hangfire.MemoryStorage;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
|
@ -14,7 +12,6 @@ using Microsoft.Extensions.Logging;
|
|||
using RdtClient.Data;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service.Services;
|
||||
|
||||
namespace RdtClient.Web
|
||||
{
|
||||
|
|
@ -83,19 +80,11 @@ namespace RdtClient.Web
|
|||
options.Cookie.Name = "SID";
|
||||
});
|
||||
|
||||
services.AddHangfire(configuration => configuration
|
||||
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
|
||||
.UseSimpleAssemblyNameTypeSerializer()
|
||||
.UseRecommendedSerializerSettings()
|
||||
.UseMemoryStorage());
|
||||
|
||||
services.AddHangfireServer();
|
||||
|
||||
DiConfig.Config(services);
|
||||
Service.DiConfig.Config(services);
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger, DataContext dataContext, IScheduler scheduler)
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger, DataContext dataContext)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
|
|
@ -107,9 +96,9 @@ namespace RdtClient.Web
|
|||
{
|
||||
await next.Invoke();
|
||||
|
||||
if (context.Response.StatusCode >= 404)
|
||||
if (context.Response.StatusCode != 200)
|
||||
{
|
||||
logger.LogWarning($"404: {context.Request.Path.Value}");
|
||||
logger.LogWarning($"{context.Response.StatusCode}: {context.Request.Path.Value}");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -118,9 +107,7 @@ namespace RdtClient.Web
|
|||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseHangfireServer();
|
||||
|
||||
|
||||
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
||||
|
||||
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
|
||||
|
|
@ -134,8 +121,6 @@ namespace RdtClient.Web
|
|||
});
|
||||
|
||||
dataContext.Migrate();
|
||||
|
||||
scheduler.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,5 +9,5 @@
|
|||
<link rel="stylesheet" href="styles.8d1160aad8efbde1424d.css"></head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime-es2015.c9afb3256f2870e161de.js" type="module"></script><script src="runtime-es5.c9afb3256f2870e161de.js" nomodule defer></script><script src="polyfills-es5.0e4e1968447fab48e788.js" nomodule defer></script><script src="polyfills-es2015.bbb42ff2e1c488ff52d5.js" type="module"></script><script src="main-es2015.949c41a1b6780ff9f3a9.js" type="module"></script><script src="main-es5.949c41a1b6780ff9f3a9.js" nomodule defer></script></body>
|
||||
<script src="runtime-es2015.1eba213af0b233498d9d.js" type="module"></script><script src="runtime-es5.1eba213af0b233498d9d.js" nomodule defer></script><script src="polyfills-es5.9e286f6d9247438cbb02.js" nomodule defer></script><script src="polyfills-es2015.690002c25ea8557bb4b0.js" type="module"></script><script src="main-es2015.86b880448cfc913cfe80.js" type="module"></script><script src="main-es5.86b880448cfc913cfe80.js" nomodule defer></script></body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETStandard,Version=v2.0/",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETStandard,Version=v2.0": {},
|
||||
".NETStandard,Version=v2.0/": {
|
||||
"RDNET/1.0.0": {
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "2.0.3",
|
||||
"Newtonsoft.Json": "12.0.3"
|
||||
},
|
||||
"runtime": {
|
||||
"RDNET.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {},
|
||||
"NETStandard.Library/2.0.3": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0"
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json/12.0.3": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Newtonsoft.Json.dll": {
|
||||
"assemblyVersion": "12.0.0.0",
|
||||
"fileVersion": "12.0.3.23909"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"RDNET/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
|
||||
"path": "microsoft.netcore.platforms/1.1.0",
|
||||
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"NETStandard.Library/2.0.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
|
||||
"path": "netstandard.library/2.0.3",
|
||||
"hashPath": "netstandard.library.2.0.3.nupkg.sha512"
|
||||
},
|
||||
"Newtonsoft.Json/12.0.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==",
|
||||
"path": "newtonsoft.json/12.0.3",
|
||||
"hashPath": "newtonsoft.json.12.0.3.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue