Move source payloads out of torrent rows
This commit is contained in:
parent
f369e97d6e
commit
81318957df
15 changed files with 1103 additions and 40 deletions
|
|
@ -11,6 +11,7 @@ public class DataContext(DbContextOptions options) : IdentityDbContext(options)
|
|||
public DbSet<Download> Downloads { get; set; }
|
||||
public DbSet<Setting> Settings { get; set; }
|
||||
public DbSet<Torrent> Torrents { get; set; }
|
||||
public DbSet<TorrentPayload> TorrentPayloads { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
|
@ -24,6 +25,12 @@ public class DataContext(DbContextOptions options) : IdentityDbContext(options)
|
|||
})
|
||||
.IsUnique();
|
||||
|
||||
builder.Entity<Torrent>()
|
||||
.HasOne(m => m.Payload)
|
||||
.WithOne(m => m.Torrent)
|
||||
.HasForeignKey<TorrentPayload>(m => m.TorrentId)
|
||||
.IsRequired(false);
|
||||
|
||||
var cascadeFKs = builder.Model.GetEntityTypes()
|
||||
.SelectMany(t => t.GetForeignKeys())
|
||||
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ public interface ITorrentData
|
|||
Task<IList<Torrent>> Get();
|
||||
Task<Torrent?> GetById(Guid torrentId);
|
||||
Task<Torrent?> GetByHash(String hash);
|
||||
Task<String?> GetPayloadContent(Guid torrentId);
|
||||
|
||||
Task<Torrent> Add(String? rdId,
|
||||
String hash,
|
||||
|
|
|
|||
|
|
@ -9,12 +9,9 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
{
|
||||
public async Task<IList<Torrent>> Get()
|
||||
{
|
||||
var torrents = await dataContext.Torrents
|
||||
.AsNoTracking()
|
||||
.AsSplitQuery()
|
||||
.Include(m => m.Downloads)
|
||||
.OrderBy(m => m.Priority ?? 9999)
|
||||
.ToListAsync();
|
||||
var torrents = await BuildTorrentQuery(includePayload: false)
|
||||
.OrderBy(m => m.Priority ?? 9999)
|
||||
.ToListAsync();
|
||||
|
||||
return torrents.OrderBy(m => m.Priority ?? 9999)
|
||||
.ThenBy(m => m.Added)
|
||||
|
|
@ -23,10 +20,8 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
|
||||
public async Task<Torrent?> GetById(Guid torrentId)
|
||||
{
|
||||
var dbTorrent = await dataContext.Torrents
|
||||
.AsNoTracking()
|
||||
.Include(m => m.Downloads)
|
||||
.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
|
||||
var dbTorrent = await BuildTorrentQuery(includePayload: true)
|
||||
.FirstOrDefaultAsync(m => m.TorrentId == torrentId);
|
||||
|
||||
if (dbTorrent == null)
|
||||
{
|
||||
|
|
@ -45,10 +40,8 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
{
|
||||
hash = hash.ToLower();
|
||||
|
||||
var dbTorrent = await dataContext.Torrents
|
||||
.AsNoTracking()
|
||||
.Include(m => m.Downloads)
|
||||
.FirstOrDefaultAsync(m => m.Hash == hash);
|
||||
var dbTorrent = await BuildTorrentQuery(includePayload: false)
|
||||
.FirstOrDefaultAsync(m => m.Hash == hash);
|
||||
|
||||
if (dbTorrent == null)
|
||||
{
|
||||
|
|
@ -63,6 +56,15 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
return dbTorrent;
|
||||
}
|
||||
|
||||
public async Task<String?> GetPayloadContent(Guid torrentId)
|
||||
{
|
||||
return await dataContext.TorrentPayloads
|
||||
.AsNoTracking()
|
||||
.Where(m => m.TorrentId == torrentId)
|
||||
.Select(m => m.Content)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<Torrent> Add(String? rdId,
|
||||
String hash,
|
||||
String? fileOrMagnetContents,
|
||||
|
|
@ -88,7 +90,6 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
DownloadManualFiles = torrent.DownloadManualFiles,
|
||||
DownloadClient = downloadClient,
|
||||
Type = downloadType,
|
||||
FileOrMagnet = fileOrMagnetContents,
|
||||
IsFile = isFile,
|
||||
Priority = torrent.Priority,
|
||||
TorrentRetryAttempts = torrent.TorrentRetryAttempts,
|
||||
|
|
@ -96,7 +97,13 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
DeleteOnError = torrent.DeleteOnError,
|
||||
Lifetime = torrent.Lifetime,
|
||||
RdStatus = torrent.RdStatus,
|
||||
RdName = torrent.RdName
|
||||
RdName = torrent.RdName,
|
||||
Payload = String.IsNullOrWhiteSpace(fileOrMagnetContents)
|
||||
? null
|
||||
: new()
|
||||
{
|
||||
Content = fileOrMagnetContents
|
||||
}
|
||||
};
|
||||
|
||||
await dataContext.Torrents.AddAsync(newTorrent);
|
||||
|
|
@ -294,6 +301,10 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
.Where(m => m.TorrentId == torrentId)
|
||||
.ExecuteDeleteAsync();
|
||||
|
||||
await dataContext.TorrentPayloads
|
||||
.Where(m => m.TorrentId == torrentId)
|
||||
.ExecuteDeleteAsync();
|
||||
|
||||
var deletedTorrents = await dataContext.Torrents
|
||||
.Where(m => m.TorrentId == torrentId)
|
||||
.ExecuteDeleteAsync();
|
||||
|
|
@ -307,4 +318,20 @@ public class TorrentData(DataContext dataContext, ILogger<TorrentData>? logger =
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private IQueryable<Torrent> BuildTorrentQuery(Boolean includePayload)
|
||||
{
|
||||
var query = dataContext.Torrents
|
||||
.AsNoTracking()
|
||||
.AsSplitQuery()
|
||||
.Include(m => m.Downloads)
|
||||
.AsQueryable();
|
||||
|
||||
if (includePayload)
|
||||
{
|
||||
query = query.Include(m => m.Payload);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
508
server/RdtClient.Data/Migrations/20260528163000_MoveSourcePayloadToPayloadTable.Designer.cs
generated
Normal file
508
server/RdtClient.Data/Migrations/20260528163000_MoveSourcePayloadToPayloadTable.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using RdtClient.Data.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20260528163000_MoveSourcePayloadToPayloadTable")]
|
||||
partial class MoveSourcePayloadToPayloadTable
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.5");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
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", (string)null);
|
||||
});
|
||||
|
||||
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")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
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")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
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", (string)null);
|
||||
});
|
||||
|
||||
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", (string)null);
|
||||
});
|
||||
|
||||
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", (string)null);
|
||||
});
|
||||
|
||||
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", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.Property<Guid>("DownloadId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Added")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Completed")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadFinished")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadQueued")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadStarted")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Error")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RemoteId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RetryCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("TorrentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingFinished")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingQueued")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingStarted")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("DownloadId");
|
||||
|
||||
b.HasIndex("TorrentId", "Path")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Downloads");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b =>
|
||||
{
|
||||
b.Property<string>("SettingId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SettingId");
|
||||
|
||||
b.ToTable("Settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Property<Guid>("TorrentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Added")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("ClientKind")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("Completed")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DeleteOnError")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DownloadAction")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DownloadClient")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DownloadManualFiles")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DownloadMinSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DownloadRetryAttempts")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Error")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ExcludeRegex")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("FilesSelected")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("FinishedAction")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("FinishedActionDelay")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Hash")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("HostDownloadAction")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("IncludeRegex")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Lifetime")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("Priority")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
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<int?>("RdStatus")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RdStatusRaw")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Retry")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RetryCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TorrentRetryAttempts")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("TorrentId");
|
||||
|
||||
b.ToTable("Torrents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.TorrentPayload", b =>
|
||||
{
|
||||
b.Property<Guid>("TorrentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("TorrentId");
|
||||
|
||||
b.ToTable("TorrentPayloads");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithMany("Downloads")
|
||||
.HasForeignKey("TorrentId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Torrent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.TorrentPayload", b =>
|
||||
{
|
||||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithOne("Payload")
|
||||
.HasForeignKey("RdtClient.Data.Models.Data.TorrentPayload", "TorrentId")
|
||||
.OnDelete(DeleteBehavior.NoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Torrent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Navigation("Downloads");
|
||||
b.Navigation("Payload");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RdtClient.Data.Migrations;
|
||||
|
||||
public partial class MoveSourcePayloadToPayloadTable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TorrentPayloads",
|
||||
columns: table => new
|
||||
{
|
||||
TorrentId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
Content = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TorrentPayloads", x => x.TorrentId);
|
||||
table.ForeignKey(
|
||||
name: "FK_TorrentPayloads_Torrents_TorrentId",
|
||||
column: x => x.TorrentId,
|
||||
principalTable: "Torrents",
|
||||
principalColumn: "TorrentId",
|
||||
onDelete: ReferentialAction.NoAction);
|
||||
});
|
||||
|
||||
migrationBuilder.Sql("""
|
||||
INSERT INTO TorrentPayloads (TorrentId, Content)
|
||||
SELECT TorrentId, FileOrMagnet
|
||||
FROM Torrents
|
||||
WHERE FileOrMagnet IS NOT NULL;
|
||||
""");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "FileOrMagnet",
|
||||
table: "Torrents");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "FileOrMagnet",
|
||||
table: "Torrents",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.Sql("""
|
||||
UPDATE Torrents
|
||||
SET FileOrMagnet = (
|
||||
SELECT Content
|
||||
FROM TorrentPayloads
|
||||
WHERE TorrentPayloads.TorrentId = Torrents.TorrentId
|
||||
)
|
||||
WHERE TorrentId IN (
|
||||
SELECT TorrentId
|
||||
FROM TorrentPayloads
|
||||
);
|
||||
""");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "TorrentPayloads");
|
||||
}
|
||||
}
|
||||
|
|
@ -324,9 +324,6 @@ namespace RdtClient.Data.Migrations
|
|||
b.Property<string>("ExcludeRegex")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileOrMagnet")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("FilesSelected")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
|
@ -411,6 +408,20 @@ namespace RdtClient.Data.Migrations
|
|||
b.ToTable("Torrents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.TorrentPayload", b =>
|
||||
{
|
||||
b.Property<Guid>("TorrentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("TorrentId");
|
||||
|
||||
b.ToTable("TorrentPayloads");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
|
|
@ -473,9 +484,21 @@ namespace RdtClient.Data.Migrations
|
|||
b.Navigation("Torrent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.TorrentPayload", b =>
|
||||
{
|
||||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithOne("Payload")
|
||||
.HasForeignKey("RdtClient.Data.Models.Data.TorrentPayload", "TorrentId")
|
||||
.OnDelete(DeleteBehavior.NoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Torrent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Navigation("Downloads");
|
||||
b.Navigation("Payload");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ public class Torrent
|
|||
public DateTimeOffset? Retry { get; set; }
|
||||
|
||||
public DownloadType Type { get; set; }
|
||||
public String? FileOrMagnet { get; set; }
|
||||
public Boolean IsFile { get; set; }
|
||||
|
||||
public Int32? Priority { get; set; }
|
||||
|
|
@ -50,6 +49,9 @@ public class Torrent
|
|||
[InverseProperty("Torrent")]
|
||||
public IList<Download> Downloads { get; set; } = [];
|
||||
|
||||
[InverseProperty(nameof(TorrentPayload.Torrent))]
|
||||
public TorrentPayload? Payload { get; set; }
|
||||
|
||||
public Provider? ClientKind { get; set; }
|
||||
public String? RdId { get; set; }
|
||||
public String? RdName { get; set; }
|
||||
|
|
|
|||
15
server/RdtClient.Data/Models/Data/TorrentPayload.cs
Normal file
15
server/RdtClient.Data/Models/Data/TorrentPayload.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RdtClient.Data.Models.Data;
|
||||
|
||||
public class TorrentPayload
|
||||
{
|
||||
[Key]
|
||||
[ForeignKey(nameof(Torrent))]
|
||||
public Guid TorrentId { get; set; }
|
||||
|
||||
public String Content { get; set; } = null!;
|
||||
|
||||
public Torrent Torrent { get; set; } = null!;
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
|
||||
namespace RdtClient.Service.Test.Regression;
|
||||
|
||||
public class TorrentPayloadDataTests : IAsyncLifetime
|
||||
{
|
||||
private readonly String _databasePath = Path.Combine(Path.GetTempPath(), $"rdt-client-payload-data-{Guid.NewGuid():N}.sqlite");
|
||||
|
||||
[Fact]
|
||||
public async Task Add_ShouldCreatePayloadRow_AndDetailReadShouldIncludePayload()
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
await context.Database.EnsureCreatedAsync();
|
||||
|
||||
var torrentData = new TorrentData(context);
|
||||
var template = new Torrent
|
||||
{
|
||||
DownloadClient = DownloadClient.Bezzad,
|
||||
HostDownloadAction = TorrentHostDownloadAction.DownloadAll,
|
||||
DownloadAction = TorrentDownloadAction.DownloadAll,
|
||||
FinishedAction = TorrentFinishedAction.None
|
||||
};
|
||||
|
||||
var added = await torrentData.Add(null,
|
||||
"hash-1",
|
||||
"magnet:?xt=urn:btih:hash-1",
|
||||
false,
|
||||
DownloadType.Torrent,
|
||||
DownloadClient.Bezzad,
|
||||
template);
|
||||
|
||||
await using var verifyContext = CreateContext();
|
||||
var payloadRow = await verifyContext.TorrentPayloads.SingleAsync(m => m.TorrentId == added.TorrentId);
|
||||
Assert.Equal("magnet:?xt=urn:btih:hash-1", payloadRow.Content);
|
||||
|
||||
var detailTorrent = await new TorrentData(verifyContext).GetById(added.TorrentId);
|
||||
Assert.NotNull(detailTorrent);
|
||||
Assert.NotNull(detailTorrent!.Payload);
|
||||
Assert.Equal("magnet:?xt=urn:btih:hash-1", detailTorrent.Payload!.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_ShouldNotMaterializePayloadNavigation()
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
await context.Database.EnsureCreatedAsync();
|
||||
|
||||
context.Torrents.Add(new Torrent
|
||||
{
|
||||
TorrentId = Guid.NewGuid(),
|
||||
Hash = "hash-2",
|
||||
Added = DateTimeOffset.UtcNow,
|
||||
Type = DownloadType.Nzb,
|
||||
IsFile = true,
|
||||
Payload = new()
|
||||
{
|
||||
Content = Convert.ToBase64String(new Byte[1024])
|
||||
}
|
||||
});
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
await using var readContext = CreateContext();
|
||||
var results = await new TorrentData(readContext).Get();
|
||||
|
||||
Assert.Single(results);
|
||||
Assert.Null(results[0].Payload);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Delete_ShouldRemovePayloadRow()
|
||||
{
|
||||
var torrentId = Guid.NewGuid();
|
||||
|
||||
await using (var context = CreateContext())
|
||||
{
|
||||
await context.Database.EnsureCreatedAsync();
|
||||
context.Torrents.Add(new Torrent
|
||||
{
|
||||
TorrentId = torrentId,
|
||||
Hash = "hash-3",
|
||||
Added = DateTimeOffset.UtcNow,
|
||||
Type = DownloadType.Torrent,
|
||||
Payload = new()
|
||||
{
|
||||
TorrentId = torrentId,
|
||||
Content = "magnet:?xt=urn:btih:hash-3"
|
||||
}
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await using (var deleteContext = CreateContext())
|
||||
{
|
||||
await new TorrentData(deleteContext).Delete(torrentId);
|
||||
}
|
||||
|
||||
await using var verifyContext = CreateContext();
|
||||
Assert.False(await verifyContext.Torrents.AnyAsync(m => m.TorrentId == torrentId));
|
||||
Assert.False(await verifyContext.TorrentPayloads.AnyAsync(m => m.TorrentId == torrentId));
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
SqliteConnection.ClearAllPools();
|
||||
|
||||
if (File.Exists(_databasePath))
|
||||
{
|
||||
File.Delete(_databasePath);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private DataContext CreateContext()
|
||||
{
|
||||
var connectionString = new SqliteConnectionStringBuilder
|
||||
{
|
||||
DataSource = _databasePath,
|
||||
ForeignKeys = true
|
||||
}.ToString();
|
||||
|
||||
var options = new DbContextOptionsBuilder<DataContext>()
|
||||
.UseSqlite(connectionString)
|
||||
.Options;
|
||||
|
||||
return new(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
|
||||
namespace RdtClient.Service.Test.Regression;
|
||||
|
||||
public class TorrentPayloadMigrationTests : IAsyncLifetime
|
||||
{
|
||||
private readonly String _databasePath = Path.Combine(Path.GetTempPath(), $"rdt-client-payload-migration-{Guid.NewGuid():N}.sqlite");
|
||||
|
||||
[Fact]
|
||||
public async Task Migrate_ShouldBackfillPayloadTable_AndDropLegacyColumn()
|
||||
{
|
||||
await using var migrationContext = CreateContext();
|
||||
var migrations = migrationContext.Database.GetMigrations().ToList();
|
||||
|
||||
Assert.True(migrations.Count >= 2);
|
||||
|
||||
var previousMigration = migrations[^2];
|
||||
var migrator = migrationContext.GetService<IMigrator>();
|
||||
|
||||
await migrator.MigrateAsync(previousMigration);
|
||||
|
||||
var torrentId = Guid.NewGuid();
|
||||
const String payload = "magnet:?xt=urn:btih:legacy-hash";
|
||||
|
||||
await migrationContext.Database.ExecuteSqlRawAsync(
|
||||
"""
|
||||
INSERT INTO Torrents (
|
||||
TorrentId,
|
||||
Hash,
|
||||
DownloadAction,
|
||||
FinishedAction,
|
||||
FinishedActionDelay,
|
||||
HostDownloadAction,
|
||||
DownloadMinSize,
|
||||
DownloadClient,
|
||||
Added,
|
||||
Type,
|
||||
FileOrMagnet,
|
||||
IsFile,
|
||||
RetryCount,
|
||||
DownloadRetryAttempts,
|
||||
TorrentRetryAttempts,
|
||||
DeleteOnError,
|
||||
Lifetime,
|
||||
RdName
|
||||
)
|
||||
VALUES (
|
||||
{0},
|
||||
{1},
|
||||
{2},
|
||||
{3},
|
||||
{4},
|
||||
{5},
|
||||
{6},
|
||||
{7},
|
||||
{8},
|
||||
{9},
|
||||
{10},
|
||||
{11},
|
||||
{12},
|
||||
{13},
|
||||
{14},
|
||||
{15},
|
||||
{16},
|
||||
{17}
|
||||
);
|
||||
""",
|
||||
torrentId,
|
||||
"legacy-hash",
|
||||
(Int32)TorrentDownloadAction.DownloadAll,
|
||||
(Int32)TorrentFinishedAction.None,
|
||||
0,
|
||||
(Int32)TorrentHostDownloadAction.DownloadAll,
|
||||
0,
|
||||
(Int32)DownloadClient.Bezzad,
|
||||
DateTimeOffset.UtcNow,
|
||||
(Int32)DownloadType.Torrent,
|
||||
payload,
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"Legacy Torrent");
|
||||
|
||||
var migrationsAssembly = migrationContext.GetService<IMigrationsAssembly>();
|
||||
var modelDiffer = migrationContext.GetService<IMigrationsModelDiffer>();
|
||||
var designTimeModel = migrationContext.GetService<IDesignTimeModel>();
|
||||
var modelRuntimeInitializer = migrationContext.GetService<IModelRuntimeInitializer>();
|
||||
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
|
||||
|
||||
Assert.NotNull(snapshotModel);
|
||||
|
||||
var initializedSnapshotModel = modelRuntimeInitializer.Initialize(snapshotModel!);
|
||||
|
||||
var pendingOperations = modelDiffer.GetDifferences(initializedSnapshotModel.GetRelationalModel(), designTimeModel.Model.GetRelationalModel())
|
||||
.Select(m => m switch
|
||||
{
|
||||
DropForeignKeyOperation drop => $"{drop.GetType().Name}:{drop.Table}.{drop.Name}",
|
||||
AddForeignKeyOperation add => $"{add.GetType().Name}:{add.Table}.{add.Name}:delete={add.OnDelete}",
|
||||
_ => m.GetType().Name
|
||||
})
|
||||
.ToList();
|
||||
|
||||
Assert.True(pendingOperations.Count == 0, $"Pending operations: {String.Join(", ", pendingOperations)}");
|
||||
|
||||
await migrator.MigrateAsync();
|
||||
|
||||
await using var verificationContext = CreateContext();
|
||||
var torrentData = new TorrentData(verificationContext);
|
||||
var torrent = await torrentData.GetById(torrentId);
|
||||
|
||||
Assert.NotNull(torrent);
|
||||
Assert.NotNull(torrent!.Payload);
|
||||
Assert.Equal(payload, torrent.Payload!.Content);
|
||||
|
||||
await using var command = verificationContext.Database.GetDbConnection().CreateCommand();
|
||||
command.CommandText = "PRAGMA table_info('Torrents');";
|
||||
|
||||
if (command.Connection!.State != System.Data.ConnectionState.Open)
|
||||
{
|
||||
await command.Connection.OpenAsync();
|
||||
}
|
||||
|
||||
var columns = new List<String>();
|
||||
|
||||
await using var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
columns.Add(reader.GetString(1));
|
||||
}
|
||||
|
||||
Assert.DoesNotContain("FileOrMagnet", columns);
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
SqliteConnection.ClearAllPools();
|
||||
|
||||
if (File.Exists(_databasePath))
|
||||
{
|
||||
File.Delete(_databasePath);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private DataContext CreateContext()
|
||||
{
|
||||
var connectionString = new SqliteConnectionStringBuilder
|
||||
{
|
||||
DataSource = _databasePath,
|
||||
ForeignKeys = true
|
||||
}.ToString();
|
||||
|
||||
var options = new DbContextOptionsBuilder<DataContext>()
|
||||
.UseSqlite(connectionString)
|
||||
.Options;
|
||||
|
||||
return new(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,10 @@ public class TorrentRunnerTest
|
|||
TorrentId = Guid.NewGuid(),
|
||||
Hash = "hash-1",
|
||||
RdName = "Torrent 1",
|
||||
FileOrMagnet = "magnet:?xt=urn:btih:hash-1",
|
||||
Payload = new()
|
||||
{
|
||||
Content = "magnet:?xt=urn:btih:hash-1"
|
||||
},
|
||||
Type = DownloadType.Torrent,
|
||||
RdStatus = TorrentStatus.Queued,
|
||||
DeleteOnError = 10,
|
||||
|
|
|
|||
|
|
@ -439,4 +439,82 @@ public class TorrentsTest
|
|||
It.IsAny<Torrent>()),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RetryTorrent_ShouldRequeueUsingStoredPayload()
|
||||
{
|
||||
var mocks = new Mocks();
|
||||
var magnetLink = "magnet:?xt=urn:btih:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&dn=RetryTorrent";
|
||||
var originalTorrent = new Torrent
|
||||
{
|
||||
TorrentId = Guid.NewGuid(),
|
||||
Hash = "legacy-hash",
|
||||
Type = DownloadType.Torrent,
|
||||
IsFile = false,
|
||||
Retry = DateTimeOffset.UtcNow,
|
||||
Payload = new()
|
||||
{
|
||||
Content = magnetLink
|
||||
},
|
||||
Downloads = [],
|
||||
DownloadClient = DownloadClient.Bezzad
|
||||
};
|
||||
|
||||
var requeuedTorrent = new Torrent
|
||||
{
|
||||
TorrentId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
mocks.TorrentDataMock.Setup(t => t.GetById(originalTorrent.TorrentId))
|
||||
.ReturnsAsync(originalTorrent);
|
||||
mocks.TorrentDataMock.Setup(t => t.UpdateComplete(It.IsAny<Guid>(),
|
||||
It.IsAny<String?>(),
|
||||
It.IsAny<DateTimeOffset?>(),
|
||||
It.IsAny<Boolean>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
mocks.TorrentDataMock.Setup(t => t.UpdateRetry(It.IsAny<Guid>(),
|
||||
It.IsAny<DateTimeOffset?>(),
|
||||
It.IsAny<Int32>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
mocks.TorrentDataMock.Setup(t => t.Delete(originalTorrent.TorrentId))
|
||||
.Returns(Task.CompletedTask);
|
||||
mocks.TorrentDataMock.Setup(t => t.GetByHash(It.IsAny<String>()))
|
||||
.ReturnsAsync((Torrent?)null);
|
||||
mocks.TorrentDataMock.Setup(t => t.Add(null,
|
||||
It.IsAny<String>(),
|
||||
magnetLink,
|
||||
false,
|
||||
DownloadType.Torrent,
|
||||
originalTorrent.DownloadClient,
|
||||
It.IsAny<Torrent>()))
|
||||
.ReturnsAsync(requeuedTorrent);
|
||||
mocks.EnricherMock.Setup(e => e.EnrichMagnetLink(magnetLink))
|
||||
.ReturnsAsync(magnetLink);
|
||||
|
||||
var torrents = new TorrentsService(mocks.TorrentsLoggerMock.Object,
|
||||
mocks.TorrentDataMock.Object,
|
||||
mocks.DownloadsMock.Object,
|
||||
mocks.ProcessFactoryMock.Object,
|
||||
new MockFileSystem(),
|
||||
mocks.EnricherMock.Object,
|
||||
null!,
|
||||
null!,
|
||||
null!,
|
||||
null!,
|
||||
null!,
|
||||
new TestSettings(),
|
||||
new TorrentRunnerState());
|
||||
|
||||
await torrents.RetryTorrent(originalTorrent.TorrentId, 3);
|
||||
|
||||
mocks.TorrentDataMock.Verify(t => t.Add(null,
|
||||
It.IsAny<String>(),
|
||||
magnetLink,
|
||||
false,
|
||||
DownloadType.Torrent,
|
||||
originalTorrent.DownloadClient,
|
||||
It.IsAny<Torrent>()),
|
||||
Times.Once);
|
||||
mocks.TorrentDataMock.Verify(t => t.UpdateRetry(requeuedTorrent.TorrentId, null, 3), Times.Once);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public static class TorrentDtoMapper
|
|||
FilesSelected = torrent.FilesSelected,
|
||||
Completed = torrent.Completed,
|
||||
Type = torrent.Type,
|
||||
FileOrMagnet = includeFileOrMagnet ? torrent.FileOrMagnet : null,
|
||||
FileOrMagnet = includeFileOrMagnet ? torrent.Payload?.Content : null,
|
||||
IsFile = torrent.IsFile,
|
||||
Priority = torrent.Priority,
|
||||
RetryCount = torrent.RetryCount,
|
||||
|
|
|
|||
|
|
@ -367,10 +367,8 @@ public class TorrentRunner(
|
|||
}
|
||||
|
||||
// Process torrents in DebridQueue
|
||||
var torrentsToAddToProvider = allTorrents
|
||||
.Where(m => m.Completed == null && m.Error == null && m.RdId == null && m.RdAdded == null && m.FileOrMagnet != null &&
|
||||
m.RdStatus == TorrentStatus.Queued)
|
||||
.ToList();
|
||||
var torrentsToAddToProvider = allTorrents.Where(m => m.Completed == null && m.Error == null && m.RdId == null && m.RdAdded == null && m.RdStatus == TorrentStatus.Queued)
|
||||
.ToList();
|
||||
|
||||
if (torrentsToAddToProvider.Count != 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ public class Torrents(
|
|||
|
||||
private async Task CopyAddedTorrent(Torrent torrent)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(settings.Current.General.CopyAddedTorrents) || String.IsNullOrWhiteSpace(torrent.FileOrMagnet) || String.IsNullOrWhiteSpace(torrent.RdName))
|
||||
if (String.IsNullOrWhiteSpace(settings.Current.General.CopyAddedTorrents) || String.IsNullOrWhiteSpace(torrent.RdName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -345,14 +345,21 @@ public class Torrents(
|
|||
fileSystem.File.Delete(copyFileName);
|
||||
}
|
||||
|
||||
var payloadContent = await GetPayloadContent(torrent);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(payloadContent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (torrent.IsFile)
|
||||
{
|
||||
var bytes = Convert.FromBase64String(torrent.FileOrMagnet);
|
||||
var bytes = Convert.FromBase64String(payloadContent);
|
||||
await fileSystem.File.WriteAllBytesAsync(copyFileName, bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
await fileSystem.File.WriteAllTextAsync(copyFileName, torrent.FileOrMagnet);
|
||||
await fileSystem.File.WriteAllTextAsync(copyFileName, payloadContent);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -366,7 +373,7 @@ public class Torrents(
|
|||
/// </summary>
|
||||
/// <param name="torrent">The torrent from the database to upload to the debrid provider</param>
|
||||
/// <returns>Updated torrent</returns>
|
||||
/// <exception cref="Exception">When RdId is not null or FileOrMagnet is null.</exception>
|
||||
/// <exception cref="Exception">When RdId is not null or the stored source payload is missing.</exception>
|
||||
public async Task DequeueFromDebridQueue(Torrent torrent)
|
||||
{
|
||||
if (torrent.RdId != null)
|
||||
|
|
@ -374,7 +381,9 @@ public class Torrents(
|
|||
throw new("Torrent already added to debrid provider, cannot dequeue");
|
||||
}
|
||||
|
||||
if (torrent.FileOrMagnet == null)
|
||||
var payloadContent = await GetPayloadContent(torrent);
|
||||
|
||||
if (payloadContent == null)
|
||||
{
|
||||
throw new("Torrent has no torrent file or magnet link");
|
||||
}
|
||||
|
|
@ -390,14 +399,14 @@ public class Torrents(
|
|||
if (torrent.Type == DownloadType.Nzb)
|
||||
{
|
||||
id = torrent.IsFile
|
||||
? await DebridClient.AddNzbFile(Convert.FromBase64String(torrent.FileOrMagnet), torrent.RdName)
|
||||
: await DebridClient.AddNzbLink(torrent.FileOrMagnet);
|
||||
? await DebridClient.AddNzbFile(Convert.FromBase64String(payloadContent), torrent.RdName)
|
||||
: await DebridClient.AddNzbLink(payloadContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
id = torrent.IsFile
|
||||
? await DebridClient.AddTorrentFile(Convert.FromBase64String(torrent.FileOrMagnet))
|
||||
: await DebridClient.AddTorrentMagnet(torrent.FileOrMagnet);
|
||||
? await DebridClient.AddTorrentFile(Convert.FromBase64String(payloadContent))
|
||||
: await DebridClient.AddTorrentMagnet(payloadContent);
|
||||
}
|
||||
|
||||
await torrentData.UpdateRdId(torrent, id);
|
||||
|
|
@ -795,7 +804,9 @@ public class Torrents(
|
|||
|
||||
await Delete(torrentId, true, true, true);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(torrent.FileOrMagnet))
|
||||
var payloadContent = await GetPayloadContent(torrent);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(payloadContent))
|
||||
{
|
||||
throw new($"Cannot re-add this torrent, original magnet or file not found");
|
||||
}
|
||||
|
|
@ -806,26 +817,26 @@ public class Torrents(
|
|||
{
|
||||
if (torrent.IsFile)
|
||||
{
|
||||
var bytes = Convert.FromBase64String(torrent.FileOrMagnet!);
|
||||
var bytes = Convert.FromBase64String(payloadContent);
|
||||
|
||||
newTorrent = await AddNzbFileToDebridQueue(bytes, torrent.RdName, torrent);
|
||||
}
|
||||
else
|
||||
{
|
||||
newTorrent = await AddNzbLinkToDebridQueue(torrent.FileOrMagnet!, torrent);
|
||||
newTorrent = await AddNzbLinkToDebridQueue(payloadContent, torrent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (torrent.IsFile)
|
||||
{
|
||||
var bytes = Convert.FromBase64String(torrent.FileOrMagnet!);
|
||||
var bytes = Convert.FromBase64String(payloadContent);
|
||||
|
||||
newTorrent = await AddFileToDebridQueue(bytes, torrent);
|
||||
}
|
||||
else
|
||||
{
|
||||
newTorrent = await AddMagnetToDebridQueue(torrent.FileOrMagnet!, torrent);
|
||||
newTorrent = await AddMagnetToDebridQueue(payloadContent, torrent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -975,6 +986,16 @@ public class Torrents(
|
|||
return settingDownloadPath;
|
||||
}
|
||||
|
||||
private async Task<String?> GetPayloadContent(Torrent torrent)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(torrent.Payload?.Content))
|
||||
{
|
||||
return torrent.Payload.Content;
|
||||
}
|
||||
|
||||
return await torrentData.GetPayloadContent(torrent.TorrentId);
|
||||
}
|
||||
|
||||
private async Task<Torrent> AddQueued(String infoHash,
|
||||
String fileOrMagnetContents,
|
||||
Boolean isFile,
|
||||
|
|
|
|||
Loading…
Reference in a new issue