diff --git a/Aberwyn/Aberwyn.csproj b/Aberwyn/Aberwyn.csproj
index 80844ed..3d6f856 100644
--- a/Aberwyn/Aberwyn.csproj
+++ b/Aberwyn/Aberwyn.csproj
@@ -9,6 +9,10 @@
Linux
+
+
+
+
diff --git a/Aberwyn/Controllers/BudgetApiController.cs b/Aberwyn/Controllers/BudgetApiController.cs
index d8c14bf..d2679bb 100644
--- a/Aberwyn/Controllers/BudgetApiController.cs
+++ b/Aberwyn/Controllers/BudgetApiController.cs
@@ -533,7 +533,7 @@ namespace Aberwyn.Controllers
return NoContent();
}
- [HttpPost("copy/{year:int}/{month:int}")]
+ /*[HttpPost("copy/{year:int}/{month:int}")]
public async Task CopyFromPreviousMonth(int year, int month)
{
var targetPeriod = await _context.BudgetPeriods
@@ -579,7 +579,7 @@ namespace Aberwyn.Controllers
await _context.SaveChangesAsync();
return Ok();
- }
+ }*/
diff --git a/Aberwyn/Controllers/RssController.cs b/Aberwyn/Controllers/RssController.cs
index 5c2a5e2..646ec95 100644
--- a/Aberwyn/Controllers/RssController.cs
+++ b/Aberwyn/Controllers/RssController.cs
@@ -1,4 +1,5 @@
using Aberwyn.Data;
+using BencodeNET.Torrents;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -107,20 +108,62 @@ public class RssController : Controller
[HttpPost]
public async Task Add(string torrentUrl)
{
- if (await _deluge.LoginAsync("deluge1"))
+ try
{
- var success = await _deluge.AddTorrentUrlAsync(torrentUrl);
- if (success)
+ if (await _deluge.LoginAsync("deluge1"))
{
- TempData["Message"] = "Torrent tillagd i Deluge!";
- return RedirectToAction("Index");
- }
- }
+ var success = await _deluge.AddTorrentUrlAsync(torrentUrl);
- TempData["Error"] = "Misslyckades att lägga till torrent.";
- return RedirectToAction("Index");
+ if (success)
+ {
+ // Hämta torrenten baserat på TorrentUrl
+ var torrent = await _context.Set()
+ .FirstOrDefaultAsync(t => t.TorrentUrl == torrentUrl);
+
+ if (torrent != null)
+ {
+ // Markera som nerladdad
+ torrent.IsDownloaded = true;
+ torrent.Status = TorrentStatus.Downloaded;
+ _context.Update(torrent);
+ }
+ else
+ {
+ // Skapa ny post om den inte finns
+ var newTorrent = new TorrentItem
+ {
+ Title = torrentUrl, // byt till korrekt namnkälla om du har
+ TorrentUrl = torrentUrl,
+ PublishDate = DateTime.UtcNow,
+ Status = TorrentStatus.Downloaded,
+ IsDownloaded = true,
+ RssSource = "Manuell", // eller sätt rätt källa
+ };
+
+ _context.Add(newTorrent);
+ }
+
+ await _context.SaveChangesAsync();
+
+ return Json(new
+ {
+ success = true,
+ message = "Torrent tillagd i Deluge och markerad som nerladdad."
+ });
+ }
+ }
+
+ return Json(new { success = false, message = "Misslyckades att lägga till torrent i Deluge." });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Fel vid tillägg av torrent");
+ return Json(new { success = false, message = "Ett fel uppstod vid tillägg av torrent." });
+ }
}
+
+
[HttpPost]
[ValidateAntiForgeryToken]
public async Task Upload(TorrentUploadViewModel model)
diff --git a/Aberwyn/Migrations/20251027205014_AddTorrentItemTable.Designer.cs b/Aberwyn/Migrations/20251027205014_AddTorrentItemTable.Designer.cs
new file mode 100644
index 0000000..104a512
--- /dev/null
+++ b/Aberwyn/Migrations/20251027205014_AddTorrentItemTable.Designer.cs
@@ -0,0 +1,1365 @@
+//
+using System;
+using Aberwyn.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Aberwyn.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20251027205014_AddTorrentItemTable")]
+ partial class AddTorrentItemTable
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "6.0.36")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("varchar(255)");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("int");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("longtext");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("datetime(6)");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("PasswordHash")
+ .HasColumnType("longtext");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("longtext");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("longtext");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.ToTable("AspNetUsers", (string)null);
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.AppSetting", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("AppSettings");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("BudgetCategoryDefinitionId")
+ .HasColumnType("int");
+
+ b.Property("BudgetPeriodId")
+ .HasColumnType("int");
+
+ b.Property("Color")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Order")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BudgetCategoryDefinitionId");
+
+ b.HasIndex("BudgetPeriodId");
+
+ b.ToTable("BudgetCategories");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetCategoryDefinition", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Color")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("BudgetCategoryDefinitions");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Amount")
+ .HasColumnType("decimal(65,30)");
+
+ b.Property("BudgetCategoryId")
+ .HasColumnType("int");
+
+ b.Property("BudgetItemDefinitionId")
+ .HasColumnType("int");
+
+ b.Property("IncludeInSummary")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("IsExpense")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Order")
+ .HasColumnType("int");
+
+ b.Property("PaymentStatus")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BudgetCategoryId");
+
+ b.HasIndex("BudgetItemDefinitionId");
+
+ b.ToTable("BudgetItems");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetItemDefinition", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("DefaultCategory")
+ .HasColumnType("longtext");
+
+ b.Property("DefaultPaymentStatus")
+ .HasColumnType("int");
+
+ b.Property("IncludeInSummary")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("IsExpense")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("BudgetItemDefinitions");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Month")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .HasColumnType("longtext");
+
+ b.Property("Order")
+ .HasColumnType("int");
+
+ b.Property("Year")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.ToTable("BudgetPeriods");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.DoughPlan", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("AntalPizzor")
+ .HasColumnType("int");
+
+ b.Property("Datum")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Jast")
+ .HasColumnType("double");
+
+ b.Property("Mjol")
+ .HasColumnType("double");
+
+ b.Property("Namn")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Olja")
+ .HasColumnType("double");
+
+ b.Property("Salt")
+ .HasColumnType("double");
+
+ b.Property("TotalDeg")
+ .HasColumnType("double");
+
+ b.Property("Vatten")
+ .HasColumnType("double");
+
+ b.Property("ViktPerPizza")
+ .HasColumnType("double");
+
+ b.HasKey("Id");
+
+ b.ToTable("DoughPlans");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.Ingredient", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Item")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("MealId")
+ .HasColumnType("int");
+
+ b.Property("Quantity")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("MealId");
+
+ b.ToTable("Ingredients");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.LabIngredient", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Item")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Quantity")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("RecipeLabEntryId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RecipeLabEntryId");
+
+ b.ToTable("LabIngredients");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Item")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Quantity")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("RecipeLabVersionId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RecipeLabVersionId");
+
+ b.ToTable("LabVersionIngredients");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.Meal", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CarbType")
+ .HasColumnType("longtext");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .HasColumnType("longtext");
+
+ b.Property("ImageData")
+ .HasColumnType("longblob");
+
+ b.Property("ImageMimeType")
+ .HasColumnType("longtext");
+
+ b.Property("ImageUrl")
+ .HasColumnType("longtext");
+
+ b.Property("Instructions")
+ .HasColumnType("longtext");
+
+ b.Property("IsAvailable")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("IsPublished")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("MealCategoryId")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("ProteinType")
+ .HasColumnType("longtext");
+
+ b.Property("RecipeUrl")
+ .HasColumnType("longtext");
+
+ b.Property("ThumbnailData")
+ .HasColumnType("longblob");
+
+ b.HasKey("Id");
+
+ b.HasIndex("MealCategoryId");
+
+ b.ToTable("Meals");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.MealCategory", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Color")
+ .HasColumnType("longtext");
+
+ b.Property("Description")
+ .HasColumnType("longtext");
+
+ b.Property("DisplayOrder")
+ .HasColumnType("int");
+
+ b.Property("Icon")
+ .HasColumnType("longtext");
+
+ b.Property("IsActive")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Slug")
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("MealCategories");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Color = "#f97316",
+ DisplayOrder = 1,
+ Icon = "🍕",
+ IsActive = true,
+ Name = "Pizza",
+ Slug = "pizza"
+ });
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.MealRating", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("MealId")
+ .HasColumnType("int");
+
+ b.Property("Rating")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("MealId");
+
+ b.ToTable("MealRatings");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.MealWish", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("IsArchived")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("IsImported")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LinkedMealId")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Recipe")
+ .HasColumnType("longtext");
+
+ b.Property("RequestedByUserId")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LinkedMealId");
+
+ b.HasIndex("RequestedByUserId");
+
+ b.ToTable("MealWishes");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.PizzaOrder", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CustomerName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("IngredientsJson")
+ .HasColumnType("longtext");
+
+ b.Property("OrderedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("PizzaName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("PizzaOrders");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Auth")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Endpoint")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("P256DH")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("PizzaOrderId")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PizzaOrderId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("PushSubscribers");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("BaseMealId")
+ .HasColumnType("int");
+
+ b.Property("Category")
+ .HasColumnType("longtext");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Inspiration")
+ .HasColumnType("longtext");
+
+ b.Property("Notes")
+ .HasColumnType("longtext");
+
+ b.Property("Rating")
+ .HasColumnType("int");
+
+ b.Property("Tags")
+ .HasColumnType("longtext");
+
+ b.Property("TestedBy")
+ .HasColumnType("longtext");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BaseMealId");
+
+ b.ToTable("RecipeLabEntries");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Instructions")
+ .HasColumnType("longtext");
+
+ b.Property("RecipeLabEntryId")
+ .HasColumnType("int");
+
+ b.Property("ResultNotes")
+ .HasColumnType("longtext");
+
+ b.Property("VersionLabel")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RecipeLabEntryId");
+
+ b.ToTable("RecipeLabVersions");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Auth")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Endpoint")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("P256DH")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("PushSubscriptions");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.TodoTask", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("AssignedTo")
+ .HasColumnType("longtext");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("IsArchived")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Priority")
+ .HasColumnType("int");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Tags")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("TodoTasks");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.UserPreferences", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("varchar(255)");
+
+ b.Property("NotifyBudget")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("NotifyMenu")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("NotifyPizza")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("UserId");
+
+ b.ToTable("UserPreferences");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.WeeklyMenu", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("BreakfastMealId")
+ .HasColumnType("int");
+
+ b.Property("Cook")
+ .HasColumnType("longtext");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Date")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DayOfWeek")
+ .HasColumnType("int");
+
+ b.Property("DinnerMealId")
+ .HasColumnType("int");
+
+ b.Property("LunchMealId")
+ .HasColumnType("int");
+
+ b.Property("WeekNumber")
+ .HasColumnType("int");
+
+ b.Property("Year")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.ToTable("WeeklyMenu", (string)null);
+ });
+
+ modelBuilder.Entity("DownloadRule", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("AutoDownload")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("CategoryFilter")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("KeywordFilter")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("MaxSize")
+ .HasColumnType("bigint");
+
+ b.Property("MinSeeders")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.ToTable("DownloadRules");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("varchar(255)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ 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("int");
+
+ b.Property("ClaimType")
+ .HasColumnType("longtext");
+
+ b.Property("ClaimValue")
+ .HasColumnType("longtext");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("ClaimType")
+ .HasColumnType("longtext");
+
+ b.Property("ClaimValue")
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("varchar(255)");
+
+ b.Property("ProviderKey")
+ .HasColumnType("varchar(255)");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("varchar(255)");
+
+ b.Property("RoleId")
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("varchar(255)");
+
+ b.Property("LoginProvider")
+ .HasColumnType("varchar(255)");
+
+ b.Property("Name")
+ .HasColumnType("varchar(255)");
+
+ b.Property("Value")
+ .HasColumnType("longtext");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("RssFeed", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("IsActive")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LastChecked")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("RssFeeds");
+ });
+
+ modelBuilder.Entity("TorrentItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Category")
+ .HasColumnType("longtext");
+
+ b.Property("Completed")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("DownloadKey")
+ .HasColumnType("longtext");
+
+ b.Property("InfoHash")
+ .HasColumnType("varchar(255)");
+
+ b.Property("IsDownloaded")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Leechers")
+ .HasColumnType("int");
+
+ b.Property("MagnetLink")
+ .HasColumnType("longtext");
+
+ b.Property("MovieName")
+ .HasColumnType("longtext");
+
+ b.Property("PublishDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("RssSource")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Seeders")
+ .HasColumnType("int");
+
+ b.Property("Size")
+ .HasColumnType("bigint");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Token")
+ .HasColumnType("longtext");
+
+ b.Property("TorrentUrl")
+ .HasColumnType("longtext");
+
+ b.Property("Year")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InfoHash")
+ .IsUnique();
+
+ b.ToTable("TorrentItems");
+ });
+
+ modelBuilder.Entity("UserTorrentSeen", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("InfoHash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("SeenDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("UserTorrentSeen");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
+ {
+ b.HasOne("Aberwyn.Models.BudgetCategoryDefinition", "Definition")
+ .WithMany()
+ .HasForeignKey("BudgetCategoryDefinitionId");
+
+ b.HasOne("Aberwyn.Models.BudgetPeriod", "BudgetPeriod")
+ .WithMany("Categories")
+ .HasForeignKey("BudgetPeriodId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("BudgetPeriod");
+
+ b.Navigation("Definition");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetItem", b =>
+ {
+ b.HasOne("Aberwyn.Models.BudgetCategory", null)
+ .WithMany("Items")
+ .HasForeignKey("BudgetCategoryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Aberwyn.Models.BudgetItemDefinition", "BudgetItemDefinition")
+ .WithMany()
+ .HasForeignKey("BudgetItemDefinitionId");
+
+ b.Navigation("BudgetItemDefinition");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.Ingredient", b =>
+ {
+ b.HasOne("Aberwyn.Models.Meal", null)
+ .WithMany("Ingredients")
+ .HasForeignKey("MealId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.LabIngredient", b =>
+ {
+ b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry")
+ .WithMany("Ingredients")
+ .HasForeignKey("RecipeLabEntryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Entry");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b =>
+ {
+ b.HasOne("Aberwyn.Models.RecipeLabVersion", "Version")
+ .WithMany("Ingredients")
+ .HasForeignKey("RecipeLabVersionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Version");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.Meal", b =>
+ {
+ b.HasOne("Aberwyn.Models.MealCategory", "Category")
+ .WithMany("Meals")
+ .HasForeignKey("MealCategoryId");
+
+ b.Navigation("Category");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.MealRating", b =>
+ {
+ b.HasOne("Aberwyn.Models.Meal", "Meal")
+ .WithMany()
+ .HasForeignKey("MealId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Meal");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.MealWish", b =>
+ {
+ b.HasOne("Aberwyn.Models.Meal", "LinkedMeal")
+ .WithMany()
+ .HasForeignKey("LinkedMealId");
+
+ b.HasOne("Aberwyn.Models.ApplicationUser", "RequestedByUser")
+ .WithMany()
+ .HasForeignKey("RequestedByUserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("LinkedMeal");
+
+ b.Navigation("RequestedByUser");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b =>
+ {
+ b.HasOne("Aberwyn.Models.PizzaOrder", "PizzaOrder")
+ .WithMany()
+ .HasForeignKey("PizzaOrderId");
+
+ b.HasOne("Aberwyn.Models.ApplicationUser", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("PizzaOrder");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b =>
+ {
+ b.HasOne("Aberwyn.Models.Meal", "BaseMeal")
+ .WithMany()
+ .HasForeignKey("BaseMealId");
+
+ b.Navigation("BaseMeal");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b =>
+ {
+ b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry")
+ .WithMany("Versions")
+ .HasForeignKey("RecipeLabEntryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Entry");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", b =>
+ {
+ b.HasOne("Aberwyn.Models.ApplicationUser", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.UserPreferences", b =>
+ {
+ b.HasOne("Aberwyn.Models.ApplicationUser", "User")
+ .WithOne("Preferences")
+ .HasForeignKey("Aberwyn.Models.UserPreferences", "UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.HasOne("Aberwyn.Models.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("Aberwyn.Models.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Aberwyn.Models.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("Aberwyn.Models.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("TorrentItem", b =>
+ {
+ b.OwnsOne("MovieMetadata", "Metadata", b1 =>
+ {
+ b1.Property("TorrentItemId")
+ .HasColumnType("int");
+
+ b1.Property("Actors")
+ .HasColumnType("longtext");
+
+ b1.Property("Director")
+ .HasColumnType("longtext");
+
+ b1.Property("Genre")
+ .HasColumnType("longtext");
+
+ b1.Property("ImdbID")
+ .HasColumnType("longtext");
+
+ b1.Property("ImdbRating")
+ .HasColumnType("longtext");
+
+ b1.Property("Plot")
+ .HasColumnType("longtext");
+
+ b1.Property("Poster")
+ .HasColumnType("longtext");
+
+ b1.Property("Providers")
+ .HasColumnType("longtext");
+
+ b1.Property("Runtime")
+ .HasColumnType("longtext");
+
+ b1.Property("Title")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b1.Property("Year")
+ .HasColumnType("longtext");
+
+ b1.HasKey("TorrentItemId");
+
+ b1.ToTable("TorrentItems");
+
+ b1.WithOwner()
+ .HasForeignKey("TorrentItemId");
+ });
+
+ b.Navigation("Metadata");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b =>
+ {
+ b.Navigation("Preferences")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
+ {
+ b.Navigation("Items");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b =>
+ {
+ b.Navigation("Categories");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.Meal", b =>
+ {
+ b.Navigation("Ingredients");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.MealCategory", b =>
+ {
+ b.Navigation("Meals");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b =>
+ {
+ b.Navigation("Ingredients");
+
+ b.Navigation("Versions");
+ });
+
+ modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b =>
+ {
+ b.Navigation("Ingredients");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Aberwyn/Migrations/20251027205014_AddTorrentItemTable.cs b/Aberwyn/Migrations/20251027205014_AddTorrentItemTable.cs
new file mode 100644
index 0000000..4551903
--- /dev/null
+++ b/Aberwyn/Migrations/20251027205014_AddTorrentItemTable.cs
@@ -0,0 +1,26 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Aberwyn.Migrations
+{
+ public partial class AddTorrentItemTable : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "IsDownloaded",
+ table: "TorrentItems",
+ type: "tinyint(1)",
+ nullable: false,
+ defaultValue: false);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "IsDownloaded",
+ table: "TorrentItems");
+ }
+ }
+}
diff --git a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs
index 8e31661..47934f2 100644
--- a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -986,6 +986,9 @@ namespace Aberwyn.Migrations
b.Property("InfoHash")
.HasColumnType("varchar(255)");
+ b.Property("IsDownloaded")
+ .HasColumnType("tinyint(1)");
+
b.Property("Leechers")
.HasColumnType("int");
diff --git a/Aberwyn/Models/TorrentInfo.cs b/Aberwyn/Models/TorrentInfo.cs
index b9cede5..b237823 100644
--- a/Aberwyn/Models/TorrentInfo.cs
+++ b/Aberwyn/Models/TorrentInfo.cs
@@ -68,6 +68,7 @@ public class TorrentItem
public string? Token { get; set; }
public MovieMetadata? Metadata { get; set; }
+ public bool IsDownloaded { get; set; }
}
diff --git a/Aberwyn/Views/Rss/_RssListPartial.cshtml b/Aberwyn/Views/Rss/_RssListPartial.cshtml
new file mode 100644
index 0000000..2a864d2
--- /dev/null
+++ b/Aberwyn/Views/Rss/_RssListPartial.cshtml
@@ -0,0 +1,118 @@
+@model RssListViewModel
+
+