summaryrefslogtreecommitdiff
path: root/Controllers/ApiMediaController.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Controllers/ApiMediaController.cs')
-rw-r--r--Controllers/ApiMediaController.cs227
1 files changed, 227 insertions, 0 deletions
diff --git a/Controllers/ApiMediaController.cs b/Controllers/ApiMediaController.cs
new file mode 100644
index 0000000..bb6c81e
--- /dev/null
+++ b/Controllers/ApiMediaController.cs
@@ -0,0 +1,227 @@
+using HyperBooru.ApiModels;
+using HyperBooru.Services;
+using HyperBooru.Util;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using System.Text.Json;
+
+namespace HyperBooru.Controllers;
+
+[ApiController]
+[Authorize]
+[Route("/api/media")]
+public class ApiMediaController : Controller {
+ private IDbContextFactory<HBContext> dbFactory;
+ private IMediaService mediaService;
+
+ public ApiMediaController(IDbContextFactory<HBContext> dbFactory, IMediaService mediaService) {
+ this.dbFactory = dbFactory;
+ this.mediaService = mediaService;
+ }
+
+ [HttpGet("{mediaId}")]
+ public async Task<IActionResult> Get([FromRoute] Guid mediaId) {
+ using var db = dbFactory.CreateDbContext();
+
+ var media = await db.Media.FirstOrDefaultAsync(m => m.Guid == mediaId);
+
+ if(media is null)
+ throw new ObjectNotFoundException([ mediaId ]);
+
+ return Ok((ApiModels.Media) media);
+ }
+
+ [HttpGet("{mediaId}/files")]
+ public async Task<IActionResult> GetUploadedFiles([FromRoute] Guid mediaId) {
+ using var db = dbFactory.CreateDbContext();
+
+ var media = await db.Media
+ .Include(m => m.UploadedFiles)
+ .FirstOrDefaultAsync(m => m.Guid == mediaId);
+
+ if(media is null)
+ throw new ObjectNotFoundException([ mediaId ]);
+
+ return Ok(media.UploadedFiles.Select(uf => (ApiModels.UploadedFile) uf).ToArray());
+ }
+
+ [HttpPatch]
+ public async Task<IActionResult> UpdateMedia([FromBody] ApiModels.Media updatedMedia) {
+ using var db = dbFactory.CreateDbContext();
+ using var transaction = await db.Database.BeginTransactionAsync();
+
+ var media = await db.Media.FirstOrDefaultAsync(m => m.Guid == updatedMedia.MediaId);
+ if(media is null)
+ throw new ObjectNotFoundException([ updatedMedia.MediaId ]);
+
+ media.ShortDescription = updatedMedia.ShortDescription?.NullIfEmpty();
+ media.LongDescription = updatedMedia.LongDescription?.NullIfEmpty();
+
+ await db.SaveChangesAsync();
+ await transaction.CommitAsync();
+
+ return Ok();
+ }
+
+ [HttpPost]
+ public IActionResult Upload() {
+ if(Request.Form.Files.Count == 0)
+ throw new ApiModels.ArgumentException("No files");
+ if(Request.Form.Files.Count > 1)
+ throw new ApiModels.ArgumentException("More than one file supplied");
+
+ var metadataString = Request.Form.Files
+ .First()
+ .Headers["X-HyperBooru-Metadata"]
+ .ElementAtOrDefault(0);
+
+ MediaUploadRequest? metadata = metadataString is null ? null :
+ JsonSerializer.Deserialize<MediaUploadRequest>(metadataString);
+
+ var formFile = Request.Form.Files.First();
+
+ var media = mediaService.Create(
+ formFile.OpenReadStream(),
+ formFile.FileName,
+ metadata?.Checksum,
+ metadata?.LastAccessTime,
+ metadata?.LastWriteTime,
+ metadata?.CreateTime,
+ metadata?.Path.NullIfEmpty(),
+ metadata?.PathType,
+ metadata?.Tags);
+
+ return Ok((ApiModels.Media) media);
+ }
+
+ [HttpDelete("{mediaId}")]
+ public void Delete([FromRoute] Guid mediaId) =>
+ mediaService.Delete(mediaId);
+
+ [HttpGet("{mediaId}/tags")]
+ public async Task<IActionResult> GetMediaTagsAsync([FromRoute] Guid mediaId) {
+ using var db = dbFactory.CreateDbContext();
+
+ var media = await db.Media
+ .Include(m => m.Tags)
+ .ThenInclude(t => t.TagDefinition)
+ .ThenInclude(td => td.ImplicitTags)
+ .FirstOrDefaultAsync(m => m.Guid == mediaId);
+ if(media is null)
+ throw new ObjectNotFoundException([ mediaId ]);
+
+ return Ok(media.Tags.Select(t => (ApiModels.TagDefinition) t.TagDefinition).ToArray());
+ }
+
+ [HttpPatch("{mediaId}/tags")]
+ public async Task<IActionResult> AddTagsToMediaAsync(
+ [FromRoute] Guid mediaId,
+ [FromBody] Guid[] tagIds) {
+
+ using var db = dbFactory.CreateDbContext();
+ using var transaction = await db.Database.BeginTransactionAsync();
+
+ var missing = new List<Guid>();
+
+ var media = await db.Media
+ .Include(m => m.Tags)
+ .ThenInclude(t => t.TagDefinition)
+ .ThenInclude(td => td.ImplicitTags)
+ .FirstOrDefaultAsync(m => m.Guid == mediaId);
+ if(media is null)
+ missing.Add(mediaId);
+
+ tagIds = tagIds.Distinct().ToArray();
+
+ var tags = await db.TagDefinitions
+ .Where(td => tagIds.Contains(td.Guid))
+ .ToArrayAsync();
+
+ missing.AddRange(tagIds.Except(tags.Select(td => td.Guid)));
+ if(missing.Any())
+ throw new ObjectNotFoundException(missing);
+
+ media.Tags.AddRange(tags
+ .Where(td => !media.Tags.Select(t => t.TagDefinition.Guid).Contains(td.Guid))
+ .Select(td => new Tag() { TagDefinition = td }));
+
+ await db.SaveChangesAsync();
+ await transaction.CommitAsync();
+
+ return Ok(media.Tags.Select(t => (ApiModels.TagDefinition) t.TagDefinition).ToArray());
+ }
+
+ [HttpPut("{mediaId}/tags")]
+ public async Task<IActionResult> ReplaceMediaTagsAsync(
+ [FromRoute] Guid mediaId,
+ [FromBody] Guid[] tagIds) {
+
+ using var db = dbFactory.CreateDbContext();
+ using var transaction = await db.Database.BeginTransactionAsync();
+
+ var missing = new List<Guid>();
+
+ var media = await db.Media
+ .Include(m => m.Tags)
+ .ThenInclude(t => t.TagDefinition)
+ .ThenInclude(td => td.ImplicitTags)
+ .FirstOrDefaultAsync(m => m.Guid == mediaId);
+ if(media is null)
+ missing.Add(mediaId);
+
+ tagIds = tagIds.Distinct().Order().ToArray();
+ var tags = await db.TagDefinitions
+ .Where(td => tagIds.Contains(td.Guid))
+ .ToArrayAsync();
+
+ missing.AddRange(tagIds.Except(tags.Select(td => td.Guid)));
+ if(missing.Any())
+ throw new ObjectNotFoundException(missing);
+
+ media.Tags.AddRange(tags
+ .Where(td => !media.Tags.Select(t => t.TagDefinition.Guid).Contains(td.Guid))
+ .Select(td => new Tag() { TagDefinition = td }));
+
+ db.Tags.RemoveRange(
+ media.Tags.Where(t => !tagIds.Contains(t.TagDefinition.Guid)));
+
+ await db.SaveChangesAsync();
+ await transaction.CommitAsync();
+
+ return Ok(media.Tags.Select(t => (ApiModels.TagDefinition) t.TagDefinition).ToArray());
+ }
+
+ [HttpPatch("{mediaId}/tags/delete")]
+ public async Task<IActionResult> DeleteTagsFromMediaAsync(
+ [FromRoute] Guid mediaId,
+ [FromBody] Guid[] tagIds) {
+
+ using var db = dbFactory.CreateDbContext();
+ using var transaction = await db.Database.BeginTransactionAsync();
+
+ var missing = new List<Guid>();
+
+ var media = await db.Media
+ .Include(m => m.Tags)
+ .ThenInclude(t => t.TagDefinition)
+ .ThenInclude(td => td.ImplicitTags)
+ .FirstOrDefaultAsync(m => m.Guid == mediaId);
+ if(media is null)
+ missing.Add(mediaId);
+
+ tagIds = tagIds.Distinct().Order().ToArray();
+
+ missing.AddRange(tagIds.Except(media.Tags.Select(t => t.TagDefinition.Guid)));
+ if(missing.Any())
+ throw new ObjectNotFoundException(missing);
+
+ db.Tags.RemoveRange(
+ media.Tags.Where(t => tagIds.Contains(t.TagDefinition.Guid)));
+
+ await db.SaveChangesAsync();
+ await transaction.CommitAsync();
+
+ return Ok(media.Tags.Select(t => (ApiModels.TagDefinition) t.TagDefinition).ToArray());
+ }
+}