diff options
| -rw-r--r-- | Controllers/MediaController.cs | 7 | ||||
| -rw-r--r-- | Media.cs | 9 | ||||
| -rw-r--r-- | Migrations/20230905172129_UploadedFileMetadata.Designer.cs | 323 | ||||
| -rw-r--r-- | Migrations/20230905172129_UploadedFileMetadata.cs | 132 | ||||
| -rw-r--r-- | Migrations/HBContextModelSnapshot.cs | 26 | ||||
| -rw-r--r-- | Pages/Gallery.razor | 7 | ||||
| -rw-r--r-- | Pages/ViewMedia.razor | 11 | ||||
| -rw-r--r-- | Services/MediaService.cs | 41 | ||||
| -rw-r--r-- | Services/OcrService.cs | 3 | ||||
| -rw-r--r-- | Services/SearchService.cs | 1 |
10 files changed, 510 insertions, 50 deletions
diff --git a/Controllers/MediaController.cs b/Controllers/MediaController.cs index 8070199..fa6e7ab 100644 --- a/Controllers/MediaController.cs +++ b/Controllers/MediaController.cs @@ -1,6 +1,7 @@ using HyperBooru.Services; using HyperBooru.Util; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace HyperBooru.Controllers; @@ -23,13 +24,15 @@ public class MediaController : Controller { [HttpGet("{mediaId}")] public IActionResult Fetch([FromRoute] Guid mediaId) { - var media = db.Media.First(m => m.Guid == mediaId); + var media = db.Media + .Include(m => m.CurrentUploadedFile) + .First(m => m.Guid == mediaId); if(media is null) return NotFound(); var fs = System.IO.File.OpenRead(mediaService.GetPath(media)); - return new FileStreamResult(fs, media.MimeType); + return new FileStreamResult(fs, media.CurrentUploadedFile.MimeType); } [HttpGet("thumb/{mediaId}")] @@ -7,12 +7,8 @@ using System.Net.NetworkInformation; namespace HyperBooru; public class Media : HBObject { - public string Checksum { get; set; } - 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 OcrData? OcrData { get; set; } public virtual UploadedFile CurrentUploadedFile { get; set; } public virtual List<UploadedFile> UploadedFiles { get; set; } = new(); @@ -34,10 +30,13 @@ public class Media : HBObject { } public class UploadedFile : HBObject { - public string OriginalChecksum { get; set; } + public string Checksum { get; set; } public bool ChecksumVerified { get; set; } = false; public string? Filename { get; set; } public long Length { get; set; } + public string MimeType { get; set; } + public int? Width { get; set; } + public int? Height { get; set; } public DateTime UploadTime { get; set; } = DateTime.UtcNow; public DateTime? LastAccessTime { get; set; } public DateTime? LastWriteTime { get; set; } diff --git a/Migrations/20230905172129_UploadedFileMetadata.Designer.cs b/Migrations/20230905172129_UploadedFileMetadata.Designer.cs new file mode 100644 index 0000000..692d0b6 --- /dev/null +++ b/Migrations/20230905172129_UploadedFileMetadata.Designer.cs @@ -0,0 +1,323 @@ +// <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("20230905172129_UploadedFileMetadata")] + partial class UploadedFileMetadata + { + /// <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.OcrData", b => + { + b.Property<int>("OcrDataId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("OcrDataId")); + + b.Property<int>("MediaId") + .HasColumnType("integer"); + + b.Property<string>("SearchableText") + .IsRequired() + .HasColumnType("text"); + + b.Property<string>("Text") + .IsRequired() + .HasColumnType("text"); + + b.Property<DateTime>("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("OcrDataId"); + + b.HasIndex("MediaId") + .IsUnique(); + + b.ToTable("OcrData"); + }); + + 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<int>("CurrentUploadedFileId") + .HasColumnType("integer"); + + b.Property<string>("LongDescription") + .HasColumnType("text"); + + b.Property<string>("ShortDescription") + .HasColumnType("text"); + + b.HasIndex("CurrentUploadedFileId") + .IsUnique(); + + 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.HasBaseType("HyperBooru.HBObject"); + + b.Property<string>("Checksum") + .IsRequired() + .HasColumnType("text"); + + b.Property<bool>("ChecksumVerified") + .HasColumnType("boolean"); + + b.Property<DateTime?>("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property<string>("Filename") + .HasColumnType("text"); + + b.Property<int?>("Height") + .HasColumnType("integer"); + + b.Property<DateTime?>("LastAccessTime") + .HasColumnType("timestamp with time zone"); + + b.Property<DateTime?>("LastWriteTime") + .HasColumnType("timestamp with time zone"); + + b.Property<long>("Length") + .HasColumnType("bigint"); + + b.Property<int>("MediaObjectId") + .HasColumnType("integer"); + + b.Property<string>("MimeType") + .IsRequired() + .HasColumnType("text"); + + b.Property<DateTime>("UploadTime") + .HasColumnType("timestamp with time zone"); + + b.Property<int?>("Width") + .HasColumnType("integer"); + + b.HasIndex("MediaObjectId"); + + b.ToTable("UploadedFiles", (string)null); + }); + + modelBuilder.Entity("HyperBooru.OcrData", b => + { + b.HasOne("HyperBooru.Media", "Media") + .WithOne("OcrData") + .HasForeignKey("HyperBooru.OcrData", "MediaId") + .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.UploadedFile", "CurrentUploadedFile") + .WithOne() + .HasForeignKey("HyperBooru.Media", "CurrentUploadedFileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("HyperBooru.HBObject", null) + .WithOne() + .HasForeignKey("HyperBooru.Media", "ObjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CurrentUploadedFile"); + }); + + 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.UploadedFile", b => + { + b.HasOne("HyperBooru.Media", "Media") + .WithMany("UploadedFiles") + .HasForeignKey("MediaObjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("HyperBooru.HBObject", null) + .WithOne() + .HasForeignKey("HyperBooru.UploadedFile", "ObjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Media"); + }); + + modelBuilder.Entity("HyperBooru.HBObject", b => + { + b.Navigation("Tags"); + }); + + modelBuilder.Entity("HyperBooru.Media", b => + { + b.Navigation("OcrData"); + + b.Navigation("UploadedFiles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20230905172129_UploadedFileMetadata.cs b/Migrations/20230905172129_UploadedFileMetadata.cs new file mode 100644 index 0000000..549ede8 --- /dev/null +++ b/Migrations/20230905172129_UploadedFileMetadata.cs @@ -0,0 +1,132 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace HyperBooru.Migrations +{ + /// <inheritdoc /> + public partial class UploadedFileMetadata : Migration + { + /// <inheritdoc /> + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "OriginalChecksum", + table: "UploadedFiles", + newName: "Checksum"); + + migrationBuilder.AddColumn<string>( + name: "MimeType", + table: "UploadedFiles", + type: "text", + nullable: true); + + migrationBuilder.AddColumn<int>( + name: "Height", + table: "UploadedFiles", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn<int>( + name: "Width", + table: "UploadedFiles", + type: "integer", + nullable: true); + + migrationBuilder.Sql(@" + UPDATE + ""UploadedFiles"" + SET + ""MimeType"" = m.""MimeType"", + ""Width"" = m.""Width"", + ""Height"" = m.""Height"" + FROM + (SELECT + ""ObjectId"", + ""MimeType"", + ""Width"", + ""Height"" + FROM + ""Media"") AS m + WHERE + ""UploadedFiles"".""MediaObjectId"" = m.""ObjectId"""); + + migrationBuilder.Sql(@" + ALTER TABLE + ""UploadedFiles"" + ALTER COLUMN + ""MimeType"" + SET NOT NULL"); + + migrationBuilder.AlterColumn<string>( + name: "MimeType", + table: "UploadedFiles", + nullable: false); + + migrationBuilder.DropColumn( + name: "Checksum", + table: "Media"); + + migrationBuilder.DropColumn( + name: "Height", + table: "Media"); + + migrationBuilder.DropColumn( + name: "MimeType", + table: "Media"); + + migrationBuilder.DropColumn( + name: "Width", + table: "Media"); + } + + /// <inheritdoc /> + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Checksum", + table: "UploadedFiles"); + + migrationBuilder.DropColumn( + name: "Height", + table: "UploadedFiles"); + + migrationBuilder.DropColumn( + name: "Width", + table: "UploadedFiles"); + + migrationBuilder.RenameColumn( + name: "MimeType", + table: "UploadedFiles", + newName: "OriginalChecksum"); + + migrationBuilder.AddColumn<string>( + name: "Checksum", + table: "Media", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn<int>( + name: "Height", + table: "Media", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn<string>( + name: "MimeType", + table: "Media", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn<int>( + name: "Width", + table: "Media", + type: "integer", + nullable: false, + defaultValue: 0); + } + } +} diff --git a/Migrations/HBContextModelSnapshot.cs b/Migrations/HBContextModelSnapshot.cs index 81ee485..a24b920 100644 --- a/Migrations/HBContextModelSnapshot.cs +++ b/Migrations/HBContextModelSnapshot.cs @@ -91,29 +91,15 @@ namespace HyperBooru.Migrations { b.HasBaseType("HyperBooru.HBObject"); - b.Property<string>("Checksum") - .IsRequired() - .HasColumnType("text"); - b.Property<int>("CurrentUploadedFileId") .HasColumnType("integer"); - 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.HasIndex("CurrentUploadedFileId") .IsUnique(); @@ -180,6 +166,10 @@ namespace HyperBooru.Migrations { b.HasBaseType("HyperBooru.HBObject"); + b.Property<string>("Checksum") + .IsRequired() + .HasColumnType("text"); + b.Property<bool>("ChecksumVerified") .HasColumnType("boolean"); @@ -189,6 +179,9 @@ namespace HyperBooru.Migrations b.Property<string>("Filename") .HasColumnType("text"); + b.Property<int?>("Height") + .HasColumnType("integer"); + b.Property<DateTime?>("LastAccessTime") .HasColumnType("timestamp with time zone"); @@ -201,13 +194,16 @@ namespace HyperBooru.Migrations b.Property<int>("MediaObjectId") .HasColumnType("integer"); - b.Property<string>("OriginalChecksum") + b.Property<string>("MimeType") .IsRequired() .HasColumnType("text"); b.Property<DateTime>("UploadTime") .HasColumnType("timestamp with time zone"); + b.Property<int?>("Width") + .HasColumnType("integer"); + b.HasIndex("MediaObjectId"); b.ToTable("UploadedFiles", (string)null); diff --git a/Pages/Gallery.razor b/Pages/Gallery.razor index 241bed0..0cf34b5 100644 --- a/Pages/Gallery.razor +++ b/Pages/Gallery.razor @@ -13,7 +13,7 @@ @foreach(var media in displayMedia) { // Precalculate thumbnail size to help the browser // lay out the images during initial page load - int width = media.Width * 200 / media.Height; + int width = (int) media.CurrentUploadedFile.Width! * 200 / (int) media.CurrentUploadedFile.Height!; <a href="/ViewMedia?m=@(media.Guid)"> <img src="/media/thumb/@(media.Guid)?h=200" width=@width height="200"/> </a> @@ -81,6 +81,7 @@ } else { queryResult = db.Media .Include(m => m.Tags) + .Include(m => m.CurrentUploadedFile) .OrderByDescending(m => m.ObjectId) .ToArray(); } @@ -104,8 +105,8 @@ private IEnumerable<Media> FilterMedia(IEnumerable<Media> media) { var nsfwTags = tagService.TagsThatImply(HBContext.NsfwTag) - .Select(td => td.ObjectId) - .ToArray(); + .Select(td => td.ObjectId) + .ToArray(); using var enumerator = media.GetEnumerator(); diff --git a/Pages/ViewMedia.razor b/Pages/ViewMedia.razor index 91f33e6..e8914a0 100644 --- a/Pages/ViewMedia.razor +++ b/Pages/ViewMedia.razor @@ -18,8 +18,8 @@ <div id="image-container"> <img src="/media/@(media.Guid)" - width=@media.Width - height=@media.Height + width=@media.CurrentUploadedFile.Width + height=@media.CurrentUploadedFile.Height onclick="toggleSidebar()"/> </div> <div id="metadata"> @@ -42,7 +42,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>Resolution: @(media.CurrentUploadedFile.Width)x@(media.CurrentUploadedFile.Height)</p> <p class="heading">Upload history</p> <hr/> <table id="uploaded-files" class="data-table"> @@ -66,10 +66,10 @@ <td title=@file.Filename>@file.Filename</td> <td title=@file.Length>@file.Length.ToBytesSI()</td> <td - title=@(file.OriginalChecksum + (file.ChecksumVerified ? " (verified)" : "")) + title=@(file.Checksum + (file.ChecksumVerified ? " (verified)" : "")) class=@(file.ChecksumVerified ? "verified" : null)> - @file.OriginalChecksum.Substring(0, 8) + @file.Checksum.Substring(0, 8) </td> </tr> } @@ -154,6 +154,7 @@ media = db.Media .Include(m => m.Tags) .ThenInclude(t => t.TagDefinition) + .Include(m => m.CurrentUploadedFile) .Include(m => m.UploadedFiles) .Include(m => m.OcrData) .First(m => m.Guid == MediaId); diff --git a/Services/MediaService.cs b/Services/MediaService.cs index cfd15f8..e573add 100644 --- a/Services/MediaService.cs +++ b/Services/MediaService.cs @@ -117,17 +117,6 @@ public class MediaService : IMediaService { if(checksum is not null && hash != checksum.ToLower()) throw new MediaCreateException("Checksum does not match"); - var fileRecord = new UploadedFile() { - Filename = fileName, - Length = fileData.Length, - OriginalChecksum = hash, - ChecksumVerified = checksum is not null, - UploadTime = DateTime.UtcNow, - LastAccessTime = lastAccessTime, - LastWriteTime = lastWriteTime, - CreateTime = createTime - }; - // Determine the MIME type fileData.Seek(0, SeekOrigin.Begin); var defs = inspector.Inspect(fileData); @@ -139,18 +128,29 @@ public class MediaService : IMediaService { fileData.Seek(0, SeekOrigin.Begin); using var magickImage = new MagickImage(fileData); - var media = db.Media - .FirstOrDefault(m => m.Checksum == hash); + var media = db.UploadedFiles + .FirstOrDefault(uf => uf.Checksum == hash)? + .Media; + + var fileRecord = new UploadedFile() { + Filename = fileName, + Length = fileData.Length, + Checksum = hash, + ChecksumVerified = checksum is not null, + MimeType = mime, + Width = magickImage.Width, + Height = magickImage.Height, + UploadTime = DateTime.UtcNow, + LastAccessTime = lastAccessTime, + LastWriteTime = lastWriteTime, + CreateTime = createTime + }; if(media is null) { var ingestTagDef = db.TagDefinitions .First(td => td.Guid == HBContext.IngestTag); media = new() { - Checksum = hash, - MimeType = mime, - Width = magickImage.Width, - Height = magickImage.Height, CurrentUploadedFile = fileRecord, UploadedFiles = new() { fileRecord @@ -244,11 +244,14 @@ public class MediaService : IMediaService { public Stream GetThumbnail(Guid media, int? width, int? height) { using var db = dbFactory.CreateDbContext(); - var m = db.Media.First(m => m.Guid == media); + + var m = db.Media + .Include(m => m.CurrentUploadedFile) + .First(m => m.Guid == media); if(m is null) throw new ObjectNotFoundException(media); - if(m.MimeType.Split("/")[0] != "image") + if(m.CurrentUploadedFile.MimeType.Split("/")[0] != "image") throw new ThumbnailException("Media object not an image", m); using var image = new MagickImage(GetPath(m)); diff --git a/Services/OcrService.cs b/Services/OcrService.cs index 2f65e43..1967234 100644 --- a/Services/OcrService.cs +++ b/Services/OcrService.cs @@ -59,9 +59,10 @@ public class OcrService : IHostedService { using var db = dbFactory.CreateDbContext(); Guid[] guids = db.Media + .Include(m => m.CurrentUploadedFile) .Include(m => m.OcrData) .Where(m => m.OcrData == null) - .Where(m => m.MimeType.Contains("image/")) + .Where(m => m.CurrentUploadedFile.MimeType.Contains("image/")) .Select(m => m.Guid) .ToArray(); db.Dispose(); diff --git a/Services/SearchService.cs b/Services/SearchService.cs index 2e097e3..7c677b2 100644 --- a/Services/SearchService.cs +++ b/Services/SearchService.cs @@ -56,6 +56,7 @@ public class SearchService : ISearchService { return db.Media .Include(m => m.Tags) + .Include(m => m.CurrentUploadedFile) .Where(m => mediaIds.Contains(m.ObjectId)) .ToArray(); } |
