This commit is contained in:
@@ -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<HomeController> logger, MenuService menuService)
|
||||
public HomeController(ApplicationDbContext applicationDbContext, ILogger<HomeController> logger, MenuService menuService, TorrentService torrentService)
|
||||
{
|
||||
_logger = logger;
|
||||
_menuService = menuService;
|
||||
_context = applicationDbContext;
|
||||
_torrentService = torrentService;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
public async Task<IActionResult> 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();
|
||||
|
||||
@@ -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<TorrentController> _logger;
|
||||
private readonly DelugeClient _deluge;
|
||||
private readonly MovieMetadataService _movieMetadataService;
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public TorrentController(
|
||||
ITorrentService torrentService,
|
||||
ILogger<TorrentController> logger,
|
||||
DelugeClient delugeClient,
|
||||
MovieMetadataService movieMetadataService)
|
||||
MovieMetadataService movieMetadataService,
|
||||
ApplicationDbContext context)
|
||||
{
|
||||
_torrentService = torrentService;
|
||||
_logger = logger;
|
||||
_deluge = delugeClient;
|
||||
_movieMetadataService = movieMetadataService;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> 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<IActionResult> Add(string torrentUrl)
|
||||
{
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Aberwyn.Data
|
||||
public DbSet<TorrentItem> TorrentItems { get; set; }
|
||||
public DbSet<RssFeed> RssFeeds { get; set; }
|
||||
public DbSet<DownloadRule> DownloadRules { get; set; }
|
||||
public DbSet<UserTorrentSeen> UserTorrentSeen { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,23 @@ public class TorrentService : ITorrentService
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public async Task<int> 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)
|
||||
{
|
||||
|
||||
1261
Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.Designer.cs
generated
Normal file
1261
Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.cs
Normal file
37
Aberwyn/Migrations/20250824172213_AddUserTorrentSeen.cs
Normal file
@@ -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<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
UserId = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
TorrentId = table.Column<int>(type: "int", nullable: false),
|
||||
SeenDate = table.Column<DateTime>(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
1262
Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.Designer.cs
generated
Normal file
1262
Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.cs
Normal file
37
Aberwyn/Migrations/20250824172938_AddUserTorrentSeenv2.cs
Normal file
@@ -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<string>(
|
||||
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<int>(
|
||||
name: "TorrentId",
|
||||
table: "UserTorrentSeen",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -950,6 +950,28 @@ namespace Aberwyn.Migrations
|
||||
b.ToTable("TorrentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("UserTorrentSeen", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("InfoHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("SeenDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UserTorrentSeen");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
|
||||
{
|
||||
b.HasOne("Aberwyn.Models.BudgetCategoryDefinition", "Definition")
|
||||
|
||||
@@ -88,15 +88,25 @@ public class MovieMetadata
|
||||
|
||||
public class TorrentListViewModel
|
||||
{
|
||||
public List<TorrentItem> Items { get; set; } = new();
|
||||
public List<TorrentListItemViewModel> 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; }
|
||||
@@ -161,3 +171,11 @@ public class DownloadRule
|
||||
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 för torrent
|
||||
public DateTime SeenDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -74,6 +74,9 @@ builder.Services.AddHttpClient<HdTorrentsTrackerScraper>();
|
||||
builder.Services.AddScoped<HdTorrentsTrackerScraper>();
|
||||
builder.Services.AddHttpClient<DelugeClient>();
|
||||
builder.Services.AddHttpClient<MovieMetadataService>();
|
||||
builder.Services.AddScoped<ITorrentService, TorrentService>();
|
||||
builder.Services.AddHttpClient<ITorrentService, TorrentService>();
|
||||
builder.Services.AddScoped<TorrentService>();
|
||||
|
||||
// Add services to the container
|
||||
builder.Services.AddControllersWithViews()
|
||||
|
||||
@@ -50,7 +50,16 @@
|
||||
}
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<li><a asp-controller="torrent" asp-action="Index"> Torrents</a></li>
|
||||
<li>
|
||||
<a asp-controller="torrent" asp-action="Index"> Torrents
|
||||
|
||||
@if (ViewBag.NewTorrentCount > 0)
|
||||
{
|
||||
<span class="new-badge">@ViewBag.NewTorrentCount</span>
|
||||
}
|
||||
</a>
|
||||
|
||||
</li>
|
||||
}
|
||||
@if (User.IsInRole("Chef"))
|
||||
{
|
||||
@@ -83,8 +92,6 @@
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<main class="main-panel">
|
||||
@RenderBody()
|
||||
@RenderSection("Scripts", required: false)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<button class="@(Model.CurrentRange == "all" ? "active" : "")" onclick="location.href='?range=all'">All time</button>
|
||||
</div>
|
||||
|
||||
<!-- Torrentlista med versioner -->
|
||||
<!-- Torrentlista -->
|
||||
<div class="torrent-list">
|
||||
<div class="torrent-header">
|
||||
<div onclick="sortBy('title')" class="@(Model.CurrentSort == "title" ? "active" : "")">Titel</div>
|
||||
@@ -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();
|
||||
|
||||
<!-- Huvudrad -->
|
||||
<div class="torrent-row torrent-group-title @(group.Versions.Count == 1 ? "last-row" : "")">
|
||||
<div class="col-title">
|
||||
<a href="@main.Metadata?.Poster" class="glightbox">
|
||||
<img src="@main.Metadata?.Poster" alt="@main.Title" class="poster" />
|
||||
</a>
|
||||
<div class="title-info">
|
||||
@if (!string.IsNullOrEmpty(main.Metadata?.Title))
|
||||
{
|
||||
<strong>@group.Title (@group.Year)</strong>
|
||||
} else
|
||||
{
|
||||
<strong>@main.Title</strong>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(main.Metadata?.Poster) && main.Metadata.Poster != "N/A")
|
||||
{
|
||||
<a href="@main.Metadata.Poster" class="glightbox">
|
||||
<img src="@main.Metadata.Poster"
|
||||
alt="@main.MovieName"
|
||||
class="poster"
|
||||
onerror="this.onerror=null; this.src='/images/fallback.jpg';" />
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<img src="/images/fallback.jpg" alt="@main.MovieName" class="poster placeholder" />
|
||||
}
|
||||
|
||||
<div class="title-info">
|
||||
|
||||
@if (!string.IsNullOrEmpty(main.Metadata?.Title))
|
||||
{
|
||||
<strong>@group.MovieName (@group.Year) </strong>
|
||||
} else
|
||||
{
|
||||
<strong>@main.Title </strong>
|
||||
}
|
||||
|
||||
@if (main.IsNew)
|
||||
{
|
||||
<img src="/images/new.png" alt="New" class="badge" />
|
||||
}
|
||||
<div class="meta">
|
||||
@if (!string.IsNullOrEmpty(main.Metadata?.Genre))
|
||||
{
|
||||
@@ -63,6 +78,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-date">
|
||||
<div class="time">@main.PublishDate.ToString("HH:mm")</div>
|
||||
<div class="date">@main.PublishDate.ToString("yyyy-MM-dd")</div>
|
||||
@@ -80,34 +96,33 @@
|
||||
<!-- Versioner -->
|
||||
@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;
|
||||
<div class="torrent-row torrent-version @(isLast ? "last-version" : "")" title="@t.Title">
|
||||
var isLast = version == lastVersion;
|
||||
<div class="torrent-row torrent-version @(isLast ? "last-version" : "")" title="@version.Title">
|
||||
<div class="col-title">
|
||||
<strong>@t.Title</strong>
|
||||
</div>
|
||||
<div>
|
||||
@t.PublishDate.ToString("HH:mm yyyy-MM-dd")
|
||||
</div>
|
||||
<div class="@(t.Seeders > 40 ? "highlight-green" : "")">
|
||||
@t.Seeders
|
||||
</div>
|
||||
<div class="highlight-red">
|
||||
@t.Leechers
|
||||
<strong>
|
||||
@version.Title
|
||||
@if (version.IsNew)
|
||||
{
|
||||
<img src="/images/new.png" alt="New" class="badge" />
|
||||
}
|
||||
</strong>
|
||||
</div>
|
||||
<div>@version.PublishDate.ToString("HH:mm yyyy-MM-dd")</div>
|
||||
<div class="@(version.Seeders > 40 ? "highlight-green" : "")">@version.Seeders</div>
|
||||
<div class="highlight-red">@version.Leechers</div>
|
||||
<div class="col-action">
|
||||
<form asp-controller="Torrent" asp-action="Add" method="post" onsubmit="return confirmDownload('@t.Title')">
|
||||
<input type="hidden" name="torrentUrl" value="@t.TorrentUrl" />
|
||||
<form asp-controller="Torrent" asp-action="Add" method="post" onsubmit="return confirmDownload('@version.Title')">
|
||||
<input type="hidden" name="torrentUrl" value="@version.TorrentUrl" />
|
||||
<button type="submit" class="btn-add btn-small">➕</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
@@ -124,12 +139,11 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js"></script>
|
||||
<script>
|
||||
const lightbox = GLightbox({
|
||||
selector: '.glightbox'
|
||||
});
|
||||
const lightbox = GLightbox({ selector: '.glightbox' });
|
||||
function sortBy(field){
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.set('sort', field);
|
||||
|
||||
@@ -465,3 +465,15 @@ body {
|
||||
background-color: #e0f0ff;
|
||||
color: #1F2C3C;
|
||||
}
|
||||
.new-badge {
|
||||
display: inline-block;
|
||||
background-color: #ff3b30; /* röd likt Facebooks notis */
|
||||
color: white;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
padding: 2px 6px;
|
||||
border-radius: 12px;
|
||||
margin-left: 0px;
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
}
|
||||
BIN
Aberwyn/wwwroot/images/new.png
Normal file
BIN
Aberwyn/wwwroot/images/new.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 863 B |
Reference in New Issue
Block a user