summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2023-08-29 13:02:41 +1000
committerJake Mannens <jake@asger.xyz>2023-08-29 13:02:41 +1000
commite9f09559b80b83e12727fd24e90d8c7f31d64856 (patch)
treeffb8f6d0f4ccc54618ff73164bcd323635dcea71
parentda640de1095395e28060211dfc9b6be82924bfd6 (diff)
Added width and height properties to media objects
-rw-r--r--Controllers/MediaController.cs6
-rw-r--r--Media.cs2
-rw-r--r--Migrations/20230829010104_MediaSize.Designer.cs264
-rw-r--r--Migrations/20230829010104_MediaSize.cs40
-rw-r--r--Migrations/HBContextModelSnapshot.cs6
-rw-r--r--Pages/ViewMedia.razor3
-rw-r--r--Services/MediaService.cs11
-rw-r--r--Todo.md1
8 files changed, 328 insertions, 5 deletions
diff --git a/Controllers/MediaController.cs b/Controllers/MediaController.cs
index 6e04670..674b406 100644
--- a/Controllers/MediaController.cs
+++ b/Controllers/MediaController.cs
@@ -56,13 +56,15 @@ public class MediaController : Controller {
if(w > image.Width || h > image.Height)
return BadRequest("Requested thumbnail size is larger than original media");
- int width = (int)(w is not null ? w : image.Width * h / image.Height);
+ #pragma warning disable CS8629
+ int width = (int)(w is not null ? w : image.Width * h / image.Height);
int height = (int)(h is not null ? h : image.Height * w / image.Width);
+ #pragma warning restore CS8629
var thumbPath = mediaService.GetPath(media, width, height);
if(!System.IO.File.Exists(thumbPath)) {
- image.Resize(new MagickGeometry(width, height));
+ image.Resize(width, height);
image.Write(thumbPath);
}
diff --git a/Media.cs b/Media.cs
index 47aa73b..d4f9b5b 100644
--- a/Media.cs
+++ b/Media.cs
@@ -11,6 +11,8 @@ public class Media : HBObject {
public string MimeType { get; set; }
public string? ShortDescription { get; set; }
public string? LongDescription { get; set; }
+ public int Width { get; set; }
+ public int Height { get; set; }
public virtual List<UploadedFile> UploadedFiles { get; set; } = new();
public bool IsIngest => Tags
diff --git a/Migrations/20230829010104_MediaSize.Designer.cs b/Migrations/20230829010104_MediaSize.Designer.cs
new file mode 100644
index 0000000..29c27bc
--- /dev/null
+++ b/Migrations/20230829010104_MediaSize.Designer.cs
@@ -0,0 +1,264 @@
+// <auto-generated />
+using System;
+using HyperBooru;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace HyperBooru.Migrations
+{
+ [DbContext(typeof(HBContext))]
+ [Migration("20230829010104_MediaSize")]
+ partial class MediaSize
+ {
+ /// <inheritdoc />
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("HyperBooru.HBObject", b =>
+ {
+ b.Property<int>("ObjectId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ObjectId"));
+
+ b.Property<Guid>("Guid")
+ .HasColumnType("uuid");
+
+ b.HasKey("ObjectId");
+
+ b.HasIndex("Guid");
+
+ b.ToTable("Objects", (string)null);
+
+ b.UseTptMappingStrategy();
+ });
+
+ modelBuilder.Entity("HyperBooru.UploadedFile", b =>
+ {
+ b.Property<int>("UploadedFileId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UploadedFileId"));
+
+ b.Property<DateTime?>("CreateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<string>("Filename")
+ .HasColumnType("text");
+
+ b.Property<DateTime?>("LastAccessTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<DateTime?>("LastWriteTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<int>("MediaObjectId")
+ .HasColumnType("integer");
+
+ b.Property<string>("OriginalChecksum")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<DateTime>("UploadTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("UploadedFileId");
+
+ b.HasIndex("MediaObjectId");
+
+ b.ToTable("UploadedFiles", (string)null);
+ });
+
+ modelBuilder.Entity("TagDefinitionTagDefinition", b =>
+ {
+ b.Property<int>("ImplicitTagsObjectId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TagDefinitionObjectId")
+ .HasColumnType("integer");
+
+ b.HasKey("ImplicitTagsObjectId", "TagDefinitionObjectId");
+
+ b.HasIndex("TagDefinitionObjectId");
+
+ b.ToTable("TagDefinitionTagDefinition");
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("Checksum")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<int>("Height")
+ .HasColumnType("integer");
+
+ b.Property<string>("LongDescription")
+ .HasColumnType("text");
+
+ b.Property<string>("MimeType")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("ShortDescription")
+ .HasColumnType("text");
+
+ b.Property<int>("Width")
+ .HasColumnType("integer");
+
+ b.ToTable("Media", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.Tag", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<DateTime>("CreateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<int>("TagDefinitionId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TargetObjectId")
+ .HasColumnType("integer");
+
+ b.HasIndex("TagDefinitionId");
+
+ b.HasIndex("TargetObjectId");
+
+ b.ToTable("Tags", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.TagDefinition", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("Alias")
+ .HasColumnType("text");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("Namespace")
+ .HasColumnType("text");
+
+ b.Property<int>("Source")
+ .HasColumnType("integer");
+
+ b.ToTable("TagDefinitions", (string)null);
+
+ b.HasData(
+ new
+ {
+ ObjectId = -1,
+ Guid = new Guid("ebdad4f8-455a-4351-8017-1d4854d6fa38"),
+ Name = "nsfw",
+ Source = 0
+ },
+ new
+ {
+ ObjectId = -2,
+ Guid = new Guid("ea212801-5bcc-4c0e-814f-fb9d30db58bc"),
+ Name = "ingest",
+ Source = 0
+ });
+ });
+
+ modelBuilder.Entity("HyperBooru.UploadedFile", b =>
+ {
+ b.HasOne("HyperBooru.Media", "Media")
+ .WithMany("UploadedFiles")
+ .HasForeignKey("MediaObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Media");
+ });
+
+ modelBuilder.Entity("TagDefinitionTagDefinition", b =>
+ {
+ b.HasOne("HyperBooru.TagDefinition", null)
+ .WithMany()
+ .HasForeignKey("ImplicitTagsObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.TagDefinition", null)
+ .WithMany()
+ .HasForeignKey("TagDefinitionObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.Media", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.Tag", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.Tag", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.TagDefinition", "TagDefinition")
+ .WithMany()
+ .HasForeignKey("TagDefinitionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.HBObject", "Target")
+ .WithMany("Tags")
+ .HasForeignKey("TargetObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("TagDefinition");
+
+ b.Navigation("Target");
+ });
+
+ modelBuilder.Entity("HyperBooru.TagDefinition", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.TagDefinition", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.HBObject", b =>
+ {
+ b.Navigation("Tags");
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.Navigation("UploadedFiles");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Migrations/20230829010104_MediaSize.cs b/Migrations/20230829010104_MediaSize.cs
new file mode 100644
index 0000000..734064c
--- /dev/null
+++ b/Migrations/20230829010104_MediaSize.cs
@@ -0,0 +1,40 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace HyperBooru.Migrations
+{
+ /// <inheritdoc />
+ public partial class MediaSize : Migration
+ {
+ /// <inheritdoc />
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn<int>(
+ name: "Height",
+ table: "Media",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.AddColumn<int>(
+ name: "Width",
+ table: "Media",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+ }
+
+ /// <inheritdoc />
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Height",
+ table: "Media");
+
+ migrationBuilder.DropColumn(
+ name: "Width",
+ table: "Media");
+ }
+ }
+}
diff --git a/Migrations/HBContextModelSnapshot.cs b/Migrations/HBContextModelSnapshot.cs
index e00f2d8..c6aef7a 100644
--- a/Migrations/HBContextModelSnapshot.cs
+++ b/Migrations/HBContextModelSnapshot.cs
@@ -102,6 +102,9 @@ namespace HyperBooru.Migrations
.IsRequired()
.HasColumnType("text");
+ b.Property<int>("Height")
+ .HasColumnType("integer");
+
b.Property<string>("LongDescription")
.HasColumnType("text");
@@ -112,6 +115,9 @@ namespace HyperBooru.Migrations
b.Property<string>("ShortDescription")
.HasColumnType("text");
+ b.Property<int>("Width")
+ .HasColumnType("integer");
+
b.ToTable("Media", (string)null);
});
diff --git a/Pages/ViewMedia.razor b/Pages/ViewMedia.razor
index 60ae43f..935b4ca 100644
--- a/Pages/ViewMedia.razor
+++ b/Pages/ViewMedia.razor
@@ -10,7 +10,7 @@
<link rel="stylesheet" type="text/css" href="@(nameof(HyperBooru)).styles.css"/>
<div id="content">
- <img src="/media/@(media.Guid)"/>
+ <img src="/media/@(media.Guid)" width=@media.Width height=@media.Height/>
<div id="metadata">
<div id="metadata-fileinfo">
@if(infoEditMode) {
@@ -30,6 +30,7 @@
<p>Title: <i>@(media.ShortDescription ?? "None")</i></p>
<p class="newlines">Description:<br/><i>@(media.LongDescription ?? "None")</i></p>
}
+ <p>Resolution: @(media.Width)x@(media.Height)</p>
<p class="heading">Upload history</p>
<hr/>
<table id="uploaded-files" class="data-table">
diff --git a/Services/MediaService.cs b/Services/MediaService.cs
index 0ca2c1b..1f2b165 100644
--- a/Services/MediaService.cs
+++ b/Services/MediaService.cs
@@ -1,4 +1,5 @@
-using Microsoft.EntityFrameworkCore;
+using ImageMagick;
+using Microsoft.EntityFrameworkCore;
using MimeDetective;
using MimeDetective.Definitions;
using System.Security.Cryptography;
@@ -120,13 +121,17 @@ public class MediaService : IMediaService {
CreateTime = createTime
};
+ // Determine the MIME type
fileData.Seek(0, SeekOrigin.Begin);
var defs = inspector.Inspect(fileData);
-
var mime = defs.ByMimeType().FirstOrDefault()?.MimeType;
if(mime is null)
throw new MediaCreateException("Unsupported file type");
+ // Read the image with ImageMagick to determine the width and height
+ fileData.Seek(0, SeekOrigin.Begin);
+ using var magickImage = new MagickImage(fileData);
+
var media = db.Media
.FirstOrDefault(m => m.Checksum == hash);
@@ -137,6 +142,8 @@ public class MediaService : IMediaService {
media = new() {
Checksum = hash,
MimeType = mime,
+ Width = magickImage.Width,
+ Height = magickImage.Height,
UploadedFiles = new() {
fileRecord
},
diff --git a/Todo.md b/Todo.md
index 9a985fa..ee1aad3 100644
--- a/Todo.md
+++ b/Todo.md
@@ -6,6 +6,7 @@
- UserService listeners don't seem to be removed after disposal
# Short-term Features
+ - Store original filesize as part of uploaded files
- Progressive page loading
- Media metadata (width, height, etc)
- Proper thumbnail generation