diff --git a/Aberwyn/Controllers/HomeController.cs b/Aberwyn/Controllers/HomeController.cs index 9ae735c..a93c08d 100644 --- a/Aberwyn/Controllers/HomeController.cs +++ b/Aberwyn/Controllers/HomeController.cs @@ -13,17 +13,19 @@ namespace Aberwyn.Controllers //private readonly BudgetService _budgetService; private readonly MenuService _menuService; private readonly ApplicationDbContext _context; + private readonly TorrentService _torrentService; // Constructor to inject dependencies - public HomeController(ApplicationDbContext applicationDbContext, ILogger logger, MenuService menuService) + public HomeController(ApplicationDbContext applicationDbContext, ILogger logger, MenuService menuService, TorrentService torrentService) { _logger = logger; _menuService = menuService; _context = applicationDbContext; + _torrentService = torrentService; } - public IActionResult Index() + public async Task Index() { var isOpen = _context.AppSettings.FirstOrDefault(x => x.Key == "RestaurantIsOpen")?.Value == "True"; ViewBag.RestaurantIsOpen = isOpen; @@ -32,11 +34,17 @@ namespace Aberwyn.Controllers var showDate = now.Hour >= 20 ? now.Date.AddDays(1) : now.Date; var todaysMenu = _menuService.GetMenuForDate(showDate); + var userId = User.Identity?.Name ?? "guest"; + // Awaita async-metoden + var newCount = await _torrentService.GetUnseenTorrentCountAsync(userId); + + ViewBag.NewTorrentCount = newCount; ViewBag.ShowDate = showDate; return View(todaysMenu); } + public IActionResult Privacy() { return View(); diff --git a/Aberwyn/Controllers/TorrentController.cs b/Aberwyn/Controllers/TorrentController.cs index 2b868cb..549216b 100644 --- a/Aberwyn/Controllers/TorrentController.cs +++ b/Aberwyn/Controllers/TorrentController.cs @@ -1,5 +1,6 @@ using Aberwyn.Data; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; public class TorrentController : Controller { @@ -7,26 +8,31 @@ public class TorrentController : Controller private readonly ILogger _logger; private readonly DelugeClient _deluge; private readonly MovieMetadataService _movieMetadataService; + private readonly ApplicationDbContext _context; public TorrentController( ITorrentService torrentService, ILogger logger, DelugeClient delugeClient, - MovieMetadataService movieMetadataService) + MovieMetadataService movieMetadataService, + ApplicationDbContext context) { _torrentService = torrentService; _logger = logger; _deluge = delugeClient; _movieMetadataService = movieMetadataService; + _context = context; } [HttpGet] public async Task Index(int page = 1, string sort = "date", string range = "all") { var pageSize = 20; + var userId = User.Identity?.Name ?? "guest"; + var torrents = await _torrentService.GetRecentTorrentsAsync(500); - // filtrera på tidsintervall + // Filtrera på tidsintervall torrents = range switch { "day" => torrents.Where(t => t.PublishDate > DateTime.UtcNow.AddDays(-1)).ToList(), @@ -35,7 +41,7 @@ public class TorrentController : Controller _ => torrents }; - // sortera + // Sortera torrents = sort switch { "seeders" => torrents.OrderByDescending(t => t.Seeders).ToList(), @@ -44,7 +50,42 @@ public class TorrentController : Controller _ => torrents.OrderByDescending(t => t.PublishDate).ToList(), }; - var pagedItems = torrents.Skip((page - 1) * pageSize).Take(pageSize).ToList(); + // Hämta sedda torrents för användaren + var seenHashes = await _context.UserTorrentSeen + .Where(x => x.UserId == userId) + .Select(x => x.InfoHash) + .ToListAsync(); + + // Bygg viewmodels med IsNew + var pagedItems = torrents + .Skip((page - 1) * pageSize) + .Take(pageSize) + .Select(t => new TorrentListItemViewModel + { + InfoHash = t.InfoHash ?? "", + Title = t.Title, + MovieName = t.MovieName ?? "", + PublishDate = t.PublishDate, + Seeders = t.Seeders, + Leechers = t.Leechers, + TorrentUrl = t.TorrentUrl, + Metadata = t.Metadata, + IsNew = t.InfoHash != null && !seenHashes.Contains(t.InfoHash) + }) + .ToList(); + + // Markera som sedda + var newSeen = pagedItems + .Where(i => i.IsNew && !string.IsNullOrEmpty(i.InfoHash)) + .Select(i => new UserTorrentSeen + { + UserId = userId, + InfoHash = i.InfoHash, + SeenDate = DateTime.UtcNow + }); + + _context.UserTorrentSeen.AddRange(newSeen); + await _context.SaveChangesAsync(); var vm = new TorrentListViewModel { @@ -58,6 +99,8 @@ public class TorrentController : Controller return View(vm); } + + [HttpPost] public async Task Add(string torrentUrl) { diff --git a/Aberwyn/Data/ApplicationDbContext.cs b/Aberwyn/Data/ApplicationDbContext.cs index ba35993..c708384 100644 --- a/Aberwyn/Data/ApplicationDbContext.cs +++ b/Aberwyn/Data/ApplicationDbContext.cs @@ -64,6 +64,7 @@ namespace Aberwyn.Data public DbSet TorrentItems { get; set; } public DbSet RssFeeds { get; set; } public DbSet DownloadRules { get; set; } + public DbSet UserTorrentSeen { get; set; } } } diff --git a/Aberwyn/Data/TorrentService.cs b/Aberwyn/Data/TorrentService.cs index b9e5b38..4b420a1 100644 --- a/Aberwyn/Data/TorrentService.cs +++ b/Aberwyn/Data/TorrentService.cs @@ -167,6 +167,23 @@ public class TorrentService : ITorrentService } return false; } + public async Task GetUnseenTorrentCountAsync(string userId) + { + // Hämta alla infohashes som användaren redan sett + var seenHashes = await _context.UserTorrentSeen + .Where(x => x.UserId == userId) + .Select(x => x.InfoHash) + .ToListAsync(); + + // Räkna alla torrents som inte finns i seenHashes och som har > 40 seeders + var count = await _context.TorrentItems + .Where(t => t.InfoHash != null + && !seenHashes.Contains(t.InfoHash) + && t.Seeders > 40) + .CountAsync(); + + return count; + } private string ConvertAnnounceToScrape(string announceUrl) { diff --git a/Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.Designer.cs b/Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.Designer.cs new file mode 100644 index 0000000..6c58c69 --- /dev/null +++ b/Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.Designer.cs @@ -0,0 +1,1261 @@ +// +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("20250824172213_AddUserTorrentSeen")] + partial class AddUserTorrentSeen + { + 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.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.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("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("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("SeenDate") + .HasColumnType("datetime(6)"); + + b.Property("TorrentId") + .HasColumnType("int"); + + 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.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/20250824172213_AddUserTorrentSeen.cs b/Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.cs new file mode 100644 index 0000000..7bfa5dd --- /dev/null +++ b/Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Aberwyn.Migrations +{ + public partial class AddUserTorrentSeen : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UserTorrentSeen", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TorrentId = table.Column(type: "int", nullable: false), + SeenDate = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserTorrentSeen", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserTorrentSeen"); + } + } +} diff --git a/Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.Designer.cs b/Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.Designer.cs new file mode 100644 index 0000000..2c208b3 --- /dev/null +++ b/Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.Designer.cs @@ -0,0 +1,1262 @@ +// +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("20250824172938_AddUserTorrentSeenv2")] + partial class AddUserTorrentSeenv2 + { + 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.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.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("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("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.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/20250824172938_AddUserTorrentSeenv2.cs b/Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.cs new file mode 100644 index 0000000..5824b01 --- /dev/null +++ b/Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Aberwyn.Migrations +{ + public partial class AddUserTorrentSeenv2 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "TorrentId", + table: "UserTorrentSeen"); + + migrationBuilder.AddColumn( + name: "InfoHash", + table: "UserTorrentSeen", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "InfoHash", + table: "UserTorrentSeen"); + + migrationBuilder.AddColumn( + name: "TorrentId", + table: "UserTorrentSeen", + type: "int", + nullable: false, + defaultValue: 0); + } + } +} diff --git a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs index f8a6ea8..38e29f3 100644 --- a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs @@ -950,6 +950,28 @@ namespace Aberwyn.Migrations 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") diff --git a/Aberwyn/Models/TorrentInfo.cs b/Aberwyn/Models/TorrentInfo.cs index add184b..cba258c 100644 --- a/Aberwyn/Models/TorrentInfo.cs +++ b/Aberwyn/Models/TorrentInfo.cs @@ -88,15 +88,25 @@ public class MovieMetadata public class TorrentListViewModel { - public List Items { get; set; } = new(); + public List Items { get; set; } = new(); public int CurrentPage { get; set; } public int TotalPages { get; set; } public string CurrentSort { get; set; } = "date"; public string CurrentRange { get; set; } = "all"; public string CurrentPeriod { get; set; } = "all"; // day/week/month/all - } - +public class TorrentListItemViewModel +{ + public string InfoHash { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public string MovieName { get; set; } = string.Empty; + public DateTime PublishDate { get; set; } + public int Seeders { get; set; } + public int Leechers { get; set; } + public string? TorrentUrl { get; set; } + public MovieMetadata? Metadata { get; set; } + public bool IsNew { get; set; } = false; +} public class JustWatchResponse { public Data data { get; set; } @@ -160,4 +170,12 @@ public class DownloadRule public int MinSeeders { get; set; } public long MaxSize { get; set; } public bool AutoDownload { get; set; } +} + +public class UserTorrentSeen +{ + public int Id { get; set; } + public string UserId { get; set; } = null!; + public string InfoHash { get; set; } = null!; // unikt fr torrent + public DateTime SeenDate { get; set; } = DateTime.UtcNow; } \ No newline at end of file diff --git a/Aberwyn/Program.cs b/Aberwyn/Program.cs index 2507674..aa6c799 100644 --- a/Aberwyn/Program.cs +++ b/Aberwyn/Program.cs @@ -74,6 +74,9 @@ builder.Services.AddHttpClient(); builder.Services.AddScoped(); builder.Services.AddHttpClient(); builder.Services.AddHttpClient(); +builder.Services.AddScoped(); +builder.Services.AddHttpClient(); +builder.Services.AddScoped(); // Add services to the container builder.Services.AddControllersWithViews() diff --git a/Aberwyn/Views/Shared/_Layout.cshtml b/Aberwyn/Views/Shared/_Layout.cshtml index fa1acb6..ed1e4bf 100644 --- a/Aberwyn/Views/Shared/_Layout.cshtml +++ b/Aberwyn/Views/Shared/_Layout.cshtml @@ -50,7 +50,16 @@ } @if (User.IsInRole("Admin")) { -
  • Torrents
  • +
  • + Torrents + + @if (ViewBag.NewTorrentCount > 0) + { + @ViewBag.NewTorrentCount + } + + +
  • } @if (User.IsInRole("Chef")) { @@ -83,8 +92,6 @@ - -
    @RenderBody() @RenderSection("Scripts", required: false) diff --git a/Aberwyn/Views/Torrent/Index.cshtml b/Aberwyn/Views/Torrent/Index.cshtml index 83a28ab..5c2f9d6 100644 --- a/Aberwyn/Views/Torrent/Index.cshtml +++ b/Aberwyn/Views/Torrent/Index.cshtml @@ -9,7 +9,7 @@ - +
    Titel
    @@ -23,32 +23,47 @@ .GroupBy(t => new { t.MovieName, t.Metadata?.Year }) .Select(g => new { - Title = g.Key.MovieName, + MovieName = g.Key.MovieName, Year = g.Key.Year, - Versions = g - .OrderByDescending(t => t.Title.Contains("Fix") || t.Title.Contains("Repack")) + Versions = g.OrderByDescending(t => t.Title.Contains("Fix") || t.Title.Contains("Repack")) .ThenByDescending(t => t.Seeders) .ToList() })) { - var showBadge = group.Versions.Count > 1; var main = group.Versions.First(); + var lastVersion = group.Versions.Last();
    - - @main.Title - -
    - @if (!string.IsNullOrEmpty(main.Metadata?.Title)) - { - @group.Title (@group.Year) - } else - { - @main.Title - } + @if (!string.IsNullOrEmpty(main.Metadata?.Poster) && main.Metadata.Poster != "N/A") + { + + @main.MovieName + + } + else + { + @main.MovieName + } +
    + + @if (!string.IsNullOrEmpty(main.Metadata?.Title)) + { + @group.MovieName (@group.Year) + } else + { + @main.Title + } + + @if (main.IsNew) + { + New + }
    @if (!string.IsNullOrEmpty(main.Metadata?.Genre)) { @@ -63,6 +78,7 @@
    +
    @main.PublishDate.ToString("HH:mm")
    @main.PublishDate.ToString("yyyy-MM-dd")
    @@ -80,34 +96,33 @@ @if (group.Versions.Count > 1) { - var lastVersion = group.Versions.Last(); - @foreach (var t in group.Versions.Skip(0)) + foreach (var version in group.Versions.Skip(1)) { - var isLast = t == lastVersion; -
    + var isLast = version == lastVersion; +
    - @t.Title -
    -
    - @t.PublishDate.ToString("HH:mm yyyy-MM-dd") -
    -
    - @t.Seeders -
    -
    - @t.Leechers + + @version.Title + @if (version.IsNew) + { + New + } +
    +
    @version.PublishDate.ToString("HH:mm yyyy-MM-dd")
    +
    @version.Seeders
    +
    @version.Leechers
    -
    - + +
    } } - } +
    @@ -124,12 +139,11 @@ } }
    +