Add a download speed tracker.
This commit is contained in:
parent
d7062de42e
commit
c96ce980d5
19 changed files with 168 additions and 479 deletions
|
|
@ -10,6 +10,8 @@ export class Download {
|
|||
public status: DownloadStatus;
|
||||
|
||||
public progress: number;
|
||||
|
||||
public speed: number;
|
||||
}
|
||||
|
||||
export enum DownloadStatus {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export class Torrent {
|
|||
|
||||
files: TorrentFile[];
|
||||
downloads: Download[];
|
||||
downloadProgress: number;
|
||||
activeDownload: Download;
|
||||
}
|
||||
|
||||
export class TorrentFile {
|
||||
|
|
|
|||
|
|
@ -10,13 +10,22 @@ export class TorrentStatusPipe implements PipeTransform {
|
|||
|
||||
transform(torrent: Torrent): string {
|
||||
switch (torrent.status) {
|
||||
case TorrentStatus.RealDebrid:
|
||||
case TorrentStatus.RealDebrid: {
|
||||
const speed = this.pipe.transform(torrent.rdSpeed, 'filesize');
|
||||
return `Downloading from RD (${torrent.rdProgress}% - ${speed}/s)`;
|
||||
}
|
||||
case TorrentStatus.WaitingForDownload:
|
||||
return `Waiting to download`;
|
||||
case TorrentStatus.Downloading:
|
||||
return `Downloading (${torrent.downloadProgress}%)`;
|
||||
case TorrentStatus.Downloading: {
|
||||
if (torrent.activeDownload != null) {
|
||||
const speed = this.pipe.transform(
|
||||
torrent.activeDownload.speed,
|
||||
'filesize'
|
||||
);
|
||||
return `Downloading (${torrent.activeDownload.progress}% - ${speed}/s)`;
|
||||
}
|
||||
return `Downloading`;
|
||||
}
|
||||
case TorrentStatus.Finished:
|
||||
return `Finished`;
|
||||
case TorrentStatus.Error:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
} from '@angular/core';
|
||||
import { Torrent } from '../models/torrent.model';
|
||||
import { TorrentService } from '../torrent.service';
|
||||
import { DownloadStatus } from '../models/download.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-torrent-table',
|
||||
|
|
@ -26,6 +27,15 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
|||
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];
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RdtClient.Data.Models.Data;
|
||||
|
|
@ -50,6 +51,15 @@ namespace RdtClient.Data.Data
|
|||
Type = "Int32",
|
||||
Value = "10"
|
||||
});
|
||||
|
||||
var cascadeFKs = builder.Model.GetEntityTypes()
|
||||
.SelectMany(t => t.GetForeignKeys())
|
||||
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
|
||||
|
||||
foreach (var fk in cascadeFKs)
|
||||
{
|
||||
fk.DeleteBehavior = DeleteBehavior.Restrict;
|
||||
}
|
||||
}
|
||||
|
||||
public void Migrate()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@ namespace RdtClient.Data.Data
|
|||
|
||||
public async Task<IList<Download>> Get()
|
||||
{
|
||||
return await _dataContext.Downloads.ToListAsync();
|
||||
return await _dataContext.Downloads
|
||||
.AsNoTracking()
|
||||
.Include(m => m.Torrent)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Download> Add(Guid torrentId, String link)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace RdtClient.Data.Data
|
|||
|
||||
public async Task<IList<Setting>> GetAll()
|
||||
{
|
||||
return await _dataContext.Settings.ToListAsync();
|
||||
return await _dataContext.Settings.AsNoTracking().ToListAsync();
|
||||
}
|
||||
|
||||
public async Task Update(IList<Setting> settings)
|
||||
|
|
@ -47,7 +47,7 @@ namespace RdtClient.Data.Data
|
|||
|
||||
public async Task<Setting> Get(String key)
|
||||
{
|
||||
return await _dataContext.Settings.FirstOrDefaultAsync(m => m.SettingId == key);
|
||||
return await _dataContext.Settings.AsNoTracking().FirstOrDefaultAsync(m => m.SettingId == key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,26 @@ namespace RdtClient.Data.Data
|
|||
|
||||
public async Task<IList<Torrent>> Get()
|
||||
{
|
||||
return await _dataContext.Torrents.ToListAsync();
|
||||
var results = await _dataContext.Torrents
|
||||
.AsNoTracking()
|
||||
.Include(m => m.Downloads)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var torrent in results)
|
||||
{
|
||||
foreach (var file in torrent.Downloads)
|
||||
{
|
||||
file.Torrent = null;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<Torrent> GetById(Guid id)
|
||||
{
|
||||
var results = await _dataContext.Torrents
|
||||
.AsNoTracking()
|
||||
.Include(m => m.Downloads)
|
||||
.FirstOrDefaultAsync(m => m.TorrentId == id);
|
||||
|
||||
|
|
@ -50,6 +64,7 @@ namespace RdtClient.Data.Data
|
|||
public async Task<Torrent> GetByHash(String hash)
|
||||
{
|
||||
var results = await _dataContext.Torrents
|
||||
.AsNoTracking()
|
||||
.Include(m => m.Downloads)
|
||||
.FirstOrDefaultAsync(m => m.Hash == hash);
|
||||
|
||||
|
|
@ -82,6 +97,8 @@ 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;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
{
|
||||
PendingDownload = 0,
|
||||
Downloading,
|
||||
Unpacking,
|
||||
Finished
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,395 +0,0 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using RdtClient.Data.Data;
|
||||
|
||||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20200403195110_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.3");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.Property<Guid>("DownloadId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Added")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("TorrentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("DownloadId");
|
||||
|
||||
b.HasIndex("TorrentId");
|
||||
|
||||
b.ToTable("Downloads");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b =>
|
||||
{
|
||||
b.Property<string>("SettingId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SettingId");
|
||||
|
||||
b.ToTable("Settings");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
SettingId = "RealDebridApiKey",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new
|
||||
{
|
||||
SettingId = "DownloadFolder",
|
||||
Type = "String",
|
||||
Value = "C:\\Downloads"
|
||||
},
|
||||
new
|
||||
{
|
||||
SettingId = "SpeedLimit",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
},
|
||||
new
|
||||
{
|
||||
SettingId = "SegmentsLimit",
|
||||
Type = "Int32",
|
||||
Value = "10"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Property<Guid>("TorrentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Hash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("RdAdded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("RdEnded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdFiles")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdHost")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("RdProgress")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("RdSeeders")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("RdSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("RdSpeed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("RdSplit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RdStatus")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("TorrentId");
|
||||
|
||||
b.ToTable("Torrents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithMany("Downloads")
|
||||
.HasForeignKey("TorrentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
public partial class Torrents_Add_Category : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Category",
|
||||
table: "Torrents",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Category",
|
||||
table: "Torrents");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,8 @@ using RdtClient.Data.Data;
|
|||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20200406172142_Torrents_Add_Category")]
|
||||
partial class Torrents_Add_Category
|
||||
[Migration("20200407174750_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
|
@ -332,7 +332,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -341,7 +341,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -350,7 +350,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -359,13 +359,13 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -374,7 +374,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -383,7 +383,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithMany("Downloads")
|
||||
.HasForeignKey("TorrentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
|
|
@ -65,6 +65,7 @@ namespace RdtClient.Data.Migrations
|
|||
{
|
||||
TorrentId = table.Column<Guid>(nullable: false),
|
||||
Hash = table.Column<string>(nullable: true),
|
||||
Category = table.Column<string>(nullable: true),
|
||||
Status = table.Column<int>(nullable: false),
|
||||
RdId = table.Column<string>(nullable: true),
|
||||
RdName = table.Column<string>(nullable: true),
|
||||
|
|
@ -102,7 +103,7 @@ namespace RdtClient.Data.Migrations
|
|||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
|
|
@ -123,7 +124,7 @@ namespace RdtClient.Data.Migrations
|
|||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
|
|
@ -143,7 +144,7 @@ namespace RdtClient.Data.Migrations
|
|||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
|
|
@ -161,13 +162,13 @@ namespace RdtClient.Data.Migrations
|
|||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
|
|
@ -187,7 +188,7 @@ namespace RdtClient.Data.Migrations
|
|||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
|
|
@ -208,7 +209,7 @@ namespace RdtClient.Data.Migrations
|
|||
column: x => x.TorrentId,
|
||||
principalTable: "Torrents",
|
||||
principalColumn: "TorrentId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
|
|
@ -224,7 +225,7 @@ namespace RdtClient.Data.Migrations
|
|||
migrationBuilder.InsertData(
|
||||
table: "Settings",
|
||||
columns: new[] { "SettingId", "Type", "Value" },
|
||||
values: new object[] { "DownloadLimit", "Int32", "1" });
|
||||
values: new object[] { "DownloadLimit", "Int32", "10" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
|
|
@ -330,7 +330,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -339,7 +339,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -348,7 +348,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -357,13 +357,13 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -372,7 +372,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
|
|
@ -381,7 +381,7 @@ namespace RdtClient.Data.Migrations
|
|||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithMany("Downloads")
|
||||
.HasForeignKey("TorrentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
|
|
|
|||
|
|
@ -23,5 +23,14 @@ namespace RdtClient.Data.Models.Data
|
|||
|
||||
[NotMapped]
|
||||
public Int32 Progress { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Int64 Speed { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateTime NextUpdate { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Int64 BytesLastUpdate { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,6 @@ namespace RdtClient.Data.Models.Data
|
|||
public Int64? RdSeeders { get; set; }
|
||||
public String RdFiles { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public Int32 DownloadProgress { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public IList<TorrentFile> Files
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
|
@ -15,7 +16,7 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
public static class DownloadManager
|
||||
{
|
||||
public static readonly ConcurrentDictionary<Guid, Download> ActiveDownloads = new ConcurrentDictionary<Guid, Download>();
|
||||
public static readonly Dictionary<Guid, Download> ActiveDownloads = new Dictionary<Guid, Download>();
|
||||
|
||||
static DownloadManager()
|
||||
{
|
||||
|
|
@ -33,11 +34,21 @@ namespace RdtClient.Service.Services
|
|||
return;
|
||||
}
|
||||
|
||||
download.Progress = 0;
|
||||
download.BytesLastUpdate = 0;
|
||||
download.NextUpdate = DateTime.UtcNow.AddSeconds(1);
|
||||
download.Speed = 0;
|
||||
|
||||
var fileUrl = download.Link;
|
||||
|
||||
var uri = new Uri(fileUrl);
|
||||
var filePath = Path.Combine(destinationFolderPath, uri.Segments.Last());
|
||||
|
||||
if (!Directory.Exists(destinationFolderPath))
|
||||
{
|
||||
Directory.CreateDirectory(destinationFolderPath);
|
||||
}
|
||||
|
||||
var webRequest = WebRequest.Create(fileUrl);
|
||||
webRequest.Method = "HEAD";
|
||||
Int64 responseLength;
|
||||
|
|
@ -56,7 +67,6 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
await using var stream = response.GetResponseStream();
|
||||
await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write);
|
||||
|
||||
var buffer = new Byte[4096];
|
||||
|
||||
while (fileStream.Length < response.ContentLength)
|
||||
|
|
@ -67,8 +77,17 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
fileStream.Write(buffer, 0, read);
|
||||
|
||||
download.Progress = (Int32) (fileStream.Length * 100 / responseLength);
|
||||
ActiveDownloads.TryAdd(download.DownloadId, download);
|
||||
ActiveDownloads[download.DownloadId].Progress = (Int32) (fileStream.Length * 100 / responseLength);
|
||||
|
||||
if (DateTime.UtcNow > ActiveDownloads[download.DownloadId]
|
||||
.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;
|
||||
|
||||
Debug.WriteLine($"{fileStream.Length}/{responseLength} {ActiveDownloads[download.DownloadId].Speed}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -77,26 +96,48 @@ namespace RdtClient.Service.Services
|
|||
}
|
||||
}
|
||||
|
||||
ActiveDownloads[download.DownloadId].Speed = 0;
|
||||
|
||||
try
|
||||
{
|
||||
await using Stream stream = File.OpenRead(filePath);
|
||||
|
||||
var reader = ReaderFactory.Open(stream);
|
||||
while (reader.MoveToNextEntry())
|
||||
if (filePath.EndsWith(".rar"))
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
await UpdateStatus(download.DownloadId, DownloadStatus.Unpacking, TorrentStatus.Downloading);
|
||||
|
||||
await using (Stream stream = File.OpenRead(filePath))
|
||||
{
|
||||
Console.WriteLine(reader.Entry.Key);
|
||||
reader.WriteEntryToDirectory(destinationFolderPath,
|
||||
new ExtractionOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
var reader = ReaderFactory.Open(stream);
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (reader.Entry.IsDirectory)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
reader.WriteEntryToDirectory(destinationFolderPath,
|
||||
new ExtractionOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var retryCount = 0;
|
||||
while (File.Exists(filePath) && retryCount < 10)
|
||||
{
|
||||
retryCount++;
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File.Delete(filePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -105,7 +146,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
await UpdateStatus(download.DownloadId, DownloadStatus.Finished, TorrentStatus.Finished);
|
||||
|
||||
ActiveDownloads.TryRemove(download.DownloadId, out _);
|
||||
ActiveDownloads.Remove(download.DownloadId);
|
||||
}
|
||||
|
||||
private static async Task UpdateStatus(Guid downloadId, DownloadStatus downloadStatus, TorrentStatus torrentStatus)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using RdtClient.Data.Enums;
|
||||
|
|
@ -52,8 +53,10 @@ namespace RdtClient.Service.Services
|
|||
return;
|
||||
}
|
||||
|
||||
var folderPath = Path.Combine(destinationFolderPath, download.Torrent.RdName);
|
||||
|
||||
download.Torrent = null;
|
||||
BackgroundJob.Enqueue(() => DownloadManager.Download(download, destinationFolderPath));
|
||||
BackgroundJob.Enqueue(() => DownloadManager.Download(download, folderPath));
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,12 +80,13 @@ namespace RdtClient.Service.Services
|
|||
|
||||
foreach (var torrent in torrents)
|
||||
{
|
||||
var downloads = DownloadManager.ActiveDownloads.Where(m => m.Value.TorrentId == torrent.TorrentId)
|
||||
.ToList();
|
||||
|
||||
if (torrent.Files.Count > 0)
|
||||
foreach (var download in torrent.Downloads)
|
||||
{
|
||||
torrent.DownloadProgress = downloads.Sum(m => m.Value.Progress) / torrent.Files.Count;
|
||||
if (DownloadManager.ActiveDownloads.TryGetValue(download.DownloadId, out var activeDownload))
|
||||
{
|
||||
download.Speed = activeDownload.Speed;
|
||||
download.Progress = activeDownload.Progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +156,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
if (rdTorrent == null)
|
||||
{
|
||||
await _downloads.DeleteForTorrent(torrent.TorrentId);
|
||||
await _torrentData.Delete(torrent.TorrentId);
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +205,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
if (torrent != null)
|
||||
{
|
||||
await _downloads.DeleteForTorrent(torrent.TorrentId);
|
||||
await _torrentData.Delete(id);
|
||||
await RdNetClient.TorrentDelete(torrent.RdId);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue