From a26e9b6a628cfd311b08e1c4d2bf612d9af9bb7c Mon Sep 17 00:00:00 2001 From: Jake Mannens Date: Fri, 25 Aug 2023 11:11:38 +1000 Subject: Moved media controller upload functionality into media service Multiple uploads can now be handled --- Services/ConfigService.cs | 29 +------- Services/MediaService.cs | 169 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 171 insertions(+), 27 deletions(-) (limited to 'Services') diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs index 814a47b..b42b80c 100644 --- a/Services/ConfigService.cs +++ b/Services/ConfigService.cs @@ -4,9 +4,7 @@ public interface IConfigService { public string DataPath { get; } public string DbConnectionString { get; } public string MediaBasePath { get; } - - public string GetPath(Media media); - public string GetPath(Media media, int width, int height); + public string ThumbnailBasePath { get; } } public class ConfigService : IConfigService { @@ -39,7 +37,8 @@ public class ConfigService : IConfigService { } public string DbConnectionString => - config.GetConnectionString("DefaultConnection"); + config.GetConnectionString("DefaultConnection") ?? + throw new HBException("Unable to get default connection string"); public string MediaBasePath => Path.Join(DataPath, "media"); @@ -52,28 +51,6 @@ public class ConfigService : IConfigService { InitDirectoryStructure(); } - public string GetPath(Media media) { - var fileInfo = new FileInfo(Path.Join( - MediaBasePath, - media.Guid.ToString().Substring(0, 2), - media.Guid.ToString().Substring(2, 2), - media.Guid.ToString())); - - Directory.CreateDirectory(fileInfo.Directory.FullName); - return fileInfo.FullName; - } - - public string GetPath(Media media, int width, int height) { - var fileInfo = new FileInfo(Path.Join( - ThumbnailBasePath, - media.Guid.ToString().Substring(0, 2), - media.Guid.ToString().Substring(2, 2), - $"{media.Guid.ToString()}-{width}-{height}")); - - Directory.CreateDirectory(fileInfo.Directory.FullName); - return fileInfo.FullName; - } - private void InitDirectoryStructure() { Directory.CreateDirectory(DataPath); Directory.CreateDirectory(MediaBasePath); diff --git a/Services/MediaService.cs b/Services/MediaService.cs index 2f84b27..460e0c7 100644 --- a/Services/MediaService.cs +++ b/Services/MediaService.cs @@ -1,4 +1,7 @@ using Microsoft.EntityFrameworkCore; +using MimeDetective; +using MimeDetective.Definitions; +using System.Security.Cryptography; namespace HyperBooru.Services; @@ -9,13 +12,43 @@ public interface IMediaService { string? longDescription); public void SetIngest(Media media, bool ingest); + + public Media Create( + Stream fileData, + string fileName, + string? checksum = null, + DateTime? lastAccessTime = null, + DateTime? lastWriteTime = null, + DateTime? createTime = null); + + public void Delete(Guid media); + public void Delete(Media media); + public string GetPath(Media media); + public string GetPath(Media media, int width, int height); + } public class MediaService : IMediaService { private IDbContextFactory dbFactory; + private IConfigService config; + + private ContentInspector inspector; + + public MediaService(IDbContextFactory dbFactory, + IConfigService config) { - public MediaService(IDbContextFactory dbFactory) => this.dbFactory = dbFactory; + this.config = config; + + ContentInspectorBuilder inspectorBuilder = new() { + Definitions = + Default.FileTypes.Images.All() + .Union(Default.FileTypes.Video.All()) + .ToList() + }; + + inspector = inspectorBuilder.Build(); + } public void SetIngest(Media media, bool ingest) { using var db = dbFactory.CreateDbContext(); @@ -57,4 +90,138 @@ public class MediaService : IMediaService { db.SaveChanges(); } + + public Media Create( + Stream fileData, + string fileName, + string? checksum = null, + DateTime? lastAccessTime = null, + DateTime? lastWriteTime = null, + DateTime? createTime = null) { + + using var db = dbFactory.CreateDbContext(); + using var transaction = db.Database.BeginTransaction(); + + if(fileData.Length == 0) + throw new MediaCreateException("File is empty"); + + // Calculate the checksum using the in-memory file contents + var hash = BitConverter + .ToString(MD5.Create().ComputeHash(fileData)) + .Replace("-", "") + .ToLower(); + + if(checksum is not null && hash != checksum.ToLower()) + throw new MediaCreateException("Checksum does not match"); + + var fileRecord = new UploadedFile() { + Filename = fileName, + OriginalChecksum = hash, + UploadTime = DateTime.UtcNow, + LastAccessTime = lastAccessTime, + LastWriteTime = lastWriteTime, + CreateTime = createTime + }; + + 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"); + + var media = db.Media + .FirstOrDefault(m => m.Checksum == hash); + + if(media is null) { + var ingestTagDef = db.TagDefinitions + .First(td => td.Guid == HBContext.IngestTag); + + media = new() { + UploadedFiles = new() { + fileRecord + }, + Tags = new() { + new() { TagDefinition = ingestTagDef } + } + }; + + using var newFile = System.IO.File.Create(GetPath(media)); + + fileData.Seek(0, SeekOrigin.Begin); + fileData.CopyTo(newFile); + newFile.Flush(); + + db.Media.Add(media); + db.SaveChanges(); + media.CurrentUploadedFile = fileRecord; + db.SaveChanges(); + } else { + media.UploadedFiles.Add(fileRecord); + db.Update(media); + db.SaveChanges(); + } + + transaction.Commit(); + + return media; + } + + public void Delete(Guid media) { + using var db = dbFactory.CreateDbContext(); + var m = db.Media.First(m => m.Guid == media); + + try { + System.IO.File.Delete( + Path.Join( + config.MediaBasePath, + m.Guid.ToString().Substring(0, 2), + m.Guid.ToString().Substring(2, 2), + m.Guid.ToString())); + } catch(IOException) {} + + try { + System.IO.Directory.Delete( + Path.Join( + config.MediaBasePath, + m.Guid.ToString().Substring(0, 2), + m.Guid.ToString().Substring(2, 2))); + } catch(IOException) {} + + try { + System.IO.Directory.Delete( + Path.Join( + config.MediaBasePath, + m.Guid.ToString().Substring(0, 2))); + } catch(IOException) {} + + db.Media.Remove(m); + db.SaveChanges(); + } + + public void Delete(Media media) => + Delete(media.Guid); + + public string GetPath(Media media) { + var fileInfo = new FileInfo( + Path.Join( + config.MediaBasePath, + media.Guid.ToString().Substring(0, 2), + media.Guid.ToString().Substring(2, 2), + media.Guid.ToString())); + + Directory.CreateDirectory(fileInfo.Directory.FullName); + return fileInfo.FullName; + } + + public string GetPath(Media media, int width, int height) { + var fileInfo = new FileInfo(Path.Join( + config.ThumbnailBasePath, + media.Guid.ToString().Substring(0, 2), + media.Guid.ToString().Substring(2, 2), + $"{media.Guid.ToString()}-{width}-{height}")); + + Directory.CreateDirectory(fileInfo.Directory.FullName); + return fileInfo.FullName; + } } -- cgit v1.3