From 007e154d8da538e86c51763e609ea1a00fc7eb9c Mon Sep 17 00:00:00 2001 From: Roger Far Date: Sat, 14 May 2022 08:09:15 -0600 Subject: [PATCH] Added missing migration, fixed watch folders. --- .../20220514010531_Nullables.Designer.cs | 460 ++++++++++++++++++ .../Migrations/20220514010531_Nullables.cs | 142 ++++++ .../Migrations/DataContextModelSnapshot.cs | 14 +- .../BackgroundServices/ProviderUpdater.cs | 2 +- .../BackgroundServices/WatchFolderChecker.cs | 25 +- .../Controllers/TorrentsController.cs | 15 +- 6 files changed, 634 insertions(+), 24 deletions(-) create mode 100644 server/RdtClient.Data/Migrations/20220514010531_Nullables.Designer.cs create mode 100644 server/RdtClient.Data/Migrations/20220514010531_Nullables.cs diff --git a/server/RdtClient.Data/Migrations/20220514010531_Nullables.Designer.cs b/server/RdtClient.Data/Migrations/20220514010531_Nullables.Designer.cs new file mode 100644 index 0000000..09a1f56 --- /dev/null +++ b/server/RdtClient.Data/Migrations/20220514010531_Nullables.Designer.cs @@ -0,0 +1,460 @@ +// +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("20220514010531_Nullables")] + partial class Nullables + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.5"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b => + { + b.Property("DownloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Added") + .HasColumnType("TEXT"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("DownloadFinished") + .HasColumnType("TEXT"); + + b.Property("DownloadQueued") + .HasColumnType("TEXT"); + + b.Property("DownloadStarted") + .HasColumnType("TEXT"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("Link") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteId") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("TorrentId") + .HasColumnType("TEXT"); + + b.Property("UnpackingFinished") + .HasColumnType("TEXT"); + + b.Property("UnpackingQueued") + .HasColumnType("TEXT"); + + b.Property("UnpackingStarted") + .HasColumnType("TEXT"); + + b.HasKey("DownloadId"); + + b.HasIndex("TorrentId"); + + b.ToTable("Downloads"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b => + { + b.Property("SettingId") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("SettingId"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b => + { + b.Property("TorrentId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Added") + .HasColumnType("TEXT"); + + b.Property("Category") + .HasColumnType("TEXT"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("DeleteOnError") + .HasColumnType("INTEGER"); + + b.Property("DownloadAction") + .HasColumnType("INTEGER"); + + b.Property("DownloadManualFiles") + .HasColumnType("TEXT"); + + b.Property("DownloadMinSize") + .HasColumnType("INTEGER"); + + b.Property("DownloadRetryAttempts") + .HasColumnType("INTEGER"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("FileOrMagnet") + .HasColumnType("TEXT"); + + b.Property("FilesSelected") + .HasColumnType("TEXT"); + + b.Property("FinishedAction") + .HasColumnType("INTEGER"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsFile") + .HasColumnType("INTEGER"); + + b.Property("Lifetime") + .HasColumnType("INTEGER"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("RdAdded") + .HasColumnType("TEXT"); + + b.Property("RdEnded") + .HasColumnType("TEXT"); + + b.Property("RdFiles") + .HasColumnType("TEXT"); + + b.Property("RdHost") + .HasColumnType("TEXT"); + + b.Property("RdId") + .HasColumnType("TEXT"); + + b.Property("RdName") + .HasColumnType("TEXT"); + + b.Property("RdProgress") + .HasColumnType("INTEGER"); + + b.Property("RdSeeders") + .HasColumnType("INTEGER"); + + b.Property("RdSize") + .HasColumnType("INTEGER"); + + b.Property("RdSpeed") + .HasColumnType("INTEGER"); + + b.Property("RdSplit") + .HasColumnType("INTEGER"); + + b.Property("RdStatus") + .HasColumnType("INTEGER"); + + b.Property("RdStatusRaw") + .HasColumnType("TEXT"); + + b.Property("Retry") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("TorrentRetryAttempts") + .HasColumnType("INTEGER"); + + b.HasKey("TorrentId"); + + b.ToTable("Torrents"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", 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", 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.Torrent", b => + { + b.Navigation("Downloads"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/server/RdtClient.Data/Migrations/20220514010531_Nullables.cs b/server/RdtClient.Data/Migrations/20220514010531_Nullables.cs new file mode 100644 index 0000000..7b975b2 --- /dev/null +++ b/server/RdtClient.Data/Migrations/20220514010531_Nullables.cs @@ -0,0 +1,142 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace RdtClient.Data.Migrations +{ + public partial class Nullables : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "RdStatus", + table: "Torrents", + type: "INTEGER", + nullable: true, + oldClrType: typeof(int), + oldType: "INTEGER"); + + migrationBuilder.AlterColumn( + name: "RdSplit", + table: "Torrents", + type: "INTEGER", + nullable: true, + oldClrType: typeof(long), + oldType: "INTEGER"); + + migrationBuilder.AlterColumn( + name: "RdSize", + table: "Torrents", + type: "INTEGER", + nullable: true, + oldClrType: typeof(long), + oldType: "INTEGER"); + + migrationBuilder.AlterColumn( + name: "RdProgress", + table: "Torrents", + type: "INTEGER", + nullable: true, + oldClrType: typeof(long), + oldType: "INTEGER"); + + migrationBuilder.AlterColumn( + name: "RdAdded", + table: "Torrents", + type: "TEXT", + nullable: true, + oldClrType: typeof(DateTimeOffset), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "Hash", + table: "Torrents", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Path", + table: "Downloads", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "RdStatus", + table: "Torrents", + type: "INTEGER", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RdSplit", + table: "Torrents", + type: "INTEGER", + nullable: false, + defaultValue: 0L, + oldClrType: typeof(long), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RdSize", + table: "Torrents", + type: "INTEGER", + nullable: false, + defaultValue: 0L, + oldClrType: typeof(long), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RdProgress", + table: "Torrents", + type: "INTEGER", + nullable: false, + defaultValue: 0L, + oldClrType: typeof(long), + oldType: "INTEGER", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "RdAdded", + table: "Torrents", + type: "TEXT", + nullable: false, + defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), + oldClrType: typeof(DateTimeOffset), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Hash", + table: "Torrents", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "Path", + table: "Downloads", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + } + } +} diff --git a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs index ccd5424..e8be86a 100644 --- a/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs +++ b/server/RdtClient.Data/Migrations/DataContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace RdtClient.Data.Migrations protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "6.0.4"); + modelBuilder.HasAnnotation("ProductVersion", "6.0.5"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -237,6 +237,7 @@ namespace RdtClient.Data.Migrations .HasColumnType("TEXT"); b.Property("Path") + .IsRequired() .HasColumnType("TEXT"); b.Property("RemoteId") @@ -320,6 +321,7 @@ namespace RdtClient.Data.Migrations .HasColumnType("INTEGER"); b.Property("Hash") + .IsRequired() .HasColumnType("TEXT"); b.Property("IsFile") @@ -331,7 +333,7 @@ namespace RdtClient.Data.Migrations b.Property("Priority") .HasColumnType("INTEGER"); - b.Property("RdAdded") + b.Property("RdAdded") .HasColumnType("TEXT"); b.Property("RdEnded") @@ -349,22 +351,22 @@ namespace RdtClient.Data.Migrations b.Property("RdName") .HasColumnType("TEXT"); - b.Property("RdProgress") + b.Property("RdProgress") .HasColumnType("INTEGER"); b.Property("RdSeeders") .HasColumnType("INTEGER"); - b.Property("RdSize") + b.Property("RdSize") .HasColumnType("INTEGER"); b.Property("RdSpeed") .HasColumnType("INTEGER"); - b.Property("RdSplit") + b.Property("RdSplit") .HasColumnType("INTEGER"); - b.Property("RdStatus") + b.Property("RdStatus") .HasColumnType("INTEGER"); b.Property("RdStatusRaw") diff --git a/server/RdtClient.Service/BackgroundServices/ProviderUpdater.cs b/server/RdtClient.Service/BackgroundServices/ProviderUpdater.cs index 1446f05..f3517f1 100644 --- a/server/RdtClient.Service/BackgroundServices/ProviderUpdater.cs +++ b/server/RdtClient.Service/BackgroundServices/ProviderUpdater.cs @@ -36,7 +36,7 @@ public class ProviderUpdater : BackgroundService { var torrents = await torrentService.Get(); - if (_nextUpdate < DateTime.UtcNow && ((torrents.Count > 0 && Settings.Get.Provider.AutoImport) || Settings.Get.Provider.AutoImport)) + if (_nextUpdate < DateTime.UtcNow && ((torrents.Count > 0 && !Settings.Get.Provider.AutoImport) || Settings.Get.Provider.AutoImport)) { _logger.LogDebug($"Updating torrent info from Real-Debrid"); diff --git a/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs b/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs index 6bb447e..1b1812b 100644 --- a/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs +++ b/server/RdtClient.Service/BackgroundServices/WatchFolderChecker.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using RdtClient.Data.Enums; using RdtClient.Data.Models.Data; using RdtClient.Service.Services; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace RdtClient.Service.BackgroundServices; @@ -30,7 +31,7 @@ public class WatchFolderChecker : BackgroundService using var scope = _serviceProvider.CreateScope(); var torrentService = scope.ServiceProvider.GetRequiredService(); - _logger.LogInformation("ProviderUpdater started."); + _logger.LogInformation("WatchFolderChecker started."); while (!stoppingToken.IsCancellationRequested) { @@ -43,6 +44,13 @@ public class WatchFolderChecker : BackgroundService continue; } + var processedStorePath = Path.Combine(Settings.Get.Watch.Path, "processed"); + + if (!Directory.Exists(processedStorePath)) + { + Directory.CreateDirectory(processedStorePath); + } + var nextCheck = _prevCheck.AddSeconds(Settings.Get.Watch.Interval); if (DateTime.UtcNow < nextCheck) @@ -52,17 +60,24 @@ public class WatchFolderChecker : BackgroundService _prevCheck = DateTime.UtcNow; - var torrentFiles = Directory.GetFiles(Settings.Get.Watch.Path, "*.torrent,*.magnet", SearchOption.TopDirectoryOnly); + var torrentFiles = Directory.GetFiles(Settings.Get.Watch.Path, "*.*", SearchOption.TopDirectoryOnly); foreach (var torrentFile in torrentFiles) { var fileInfo = new FileInfo(torrentFile); + if (fileInfo.Extension != ".magnet" && fileInfo.Extension != ".torrent") + { + continue; + } + if (IsFileLocked(fileInfo)) { continue; } + _logger.Log(LogLevel.Debug, "Processing {torrentFile}", torrentFile); + var torrent = new Torrent { Category = Settings.Get.Watch.Default.Category, @@ -86,8 +101,12 @@ public class WatchFolderChecker : BackgroundService var magnetLink = await File.ReadAllTextAsync(torrentFile, stoppingToken); await torrentService.UploadMagnet(magnetLink, torrent); } - } + var processedPath = Path.Combine(processedStorePath, fileInfo.Name); + File.Move(torrentFile, processedPath); + + _logger.Log(LogLevel.Debug, "Moved {torrentFile} to {processedPath}", torrentFile, processedPath); + } } catch (Exception ex) { diff --git a/server/RdtClient.Web/Controllers/TorrentsController.cs b/server/RdtClient.Web/Controllers/TorrentsController.cs index b625db5..6d26663 100644 --- a/server/RdtClient.Web/Controllers/TorrentsController.cs +++ b/server/RdtClient.Web/Controllers/TorrentsController.cs @@ -71,13 +71,6 @@ public class TorrentsController : Controller [ModelBinder(BinderType = typeof(JsonModelBinder))] TorrentControllerUploadFileRequest? formData) { - if (!ModelState.IsValid) - { - var errors = ModelState.Select(x => x.Value?.Errors.Select(m => m.ErrorMessage)).ToList(); - return BadRequest(errors); - } - - if (file == null || file.Length <= 0) { return BadRequest("Invalid torrent file"); @@ -109,13 +102,7 @@ public class TorrentsController : Controller { return BadRequest(); } - - if (!ModelState.IsValid) - { - var errors = ModelState.Select(x => x.Value?.Errors.Select(m => m.ErrorMessage)).ToList(); - return BadRequest(errors); - } - + if (String.IsNullOrEmpty(request.MagnetLink)) { return BadRequest("Invalid magnet link");