Files
MilkyShots/Lactose/Repositories/MediaRepository.cs
T
REDCODE 5b4e2b9afc feat(media): repurpose GIFs as video and serve converted mp4 when available
- Classify .gif as EAssetType.Video via MimeTypes (moved image/gif to the Video registry)
- Add MediaRepository.GetConvertedData, served by the existing media/{id} endpoint
  when a converted file exists on disk (with a fallback + warning log if missing)
- Expose the converted MIME type on AssetPreviewDto/AlbumAssetPreviewDto
  so the frontend can detect video assets from a single MimeType field
2026-08-20 23:58:48 +02:00

115 lines
4.4 KiB
C#

using Butter.Dtos;
using Butter.Types;
using Lactose.Context;
using Lactose.Models;
namespace Lactose.Repositories;
/// <inheritdoc />
public class MediaRepository(LactoseDbContext context, IPersonRepository personRepository) : IMediaRepository {
/// <summary>
/// Trys to get the asset with the given ID, and checks visibility for the requesting user.
/// Public assets are accessible to all; Protected assets require authentication;
/// Private assets require the user to be the uploader, a curator+, or a maintainer
/// of the cosplayer whose album contains the asset.
/// </summary>
/// <param name="id">Asset ID</param>
/// <param name="userId">UserID we are trying to get this as</param>
/// <param name="accessLevel">The requesting user's access level</param>
/// <returns></returns>
Asset? GetVisibleAsset(Guid id, Guid? userId, EAccessLevel accessLevel) {
var query = context.Assets.Where(asset => asset.Id == id);
if (accessLevel >= EAccessLevel.Admin)
return query.FirstOrDefault();
if (accessLevel >= EAccessLevel.Curator)
return query.FirstOrDefault(asset =>
asset.DeletedAt == null || asset.UploadedBy == userId);
if (accessLevel == EAccessLevel.Maintainer && userId.HasValue) {
var asset = query
.AsSplitQuery()
.Include(a => a.Albums)
.FirstOrDefault();
if (asset == null || asset.DeletedAt != null) return null;
if (asset.Visibility == EVisibility.Public) return asset;
if (asset.Visibility == EVisibility.Protected) return asset;
if (asset.UploadedBy == userId) return asset;
if (asset.Albums != null && asset.Albums.Any(a =>
a.PersonOwnerId.HasValue && personRepository.IsMaintainerOf(userId.Value, a.PersonOwnerId.Value)))
return asset;
return null;
}
return query.FirstOrDefault(asset =>
asset.DeletedAt == null && (
asset.Visibility == EVisibility.Public ||
(asset.Visibility == EVisibility.Protected && userId.HasValue) ||
(asset.Visibility == EVisibility.Private && userId.HasValue && asset.UploadedBy == userId)
)
);
}
/// <inheritdoc />
public MediaDto? GetOriginalData(Guid id, Guid? userId, EAccessLevel accessLevel = EAccessLevel.User) {
var media = GetVisibleAsset(id, userId, accessLevel);
if (media == null) return null;
return new MediaDto {
MimeType = media.MimeType,
Path = media.OriginalPath
};
}
//TODO: for Thumbnails the MimeType should be the correct one (depending on which format is used for thumbnails)
/// <inheritdoc />
public MediaDto? GetThumbData(Guid id, Guid? userId, EAccessLevel accessLevel = EAccessLevel.User) {
var media = GetVisibleAsset(id, userId, accessLevel);
if(media == null) return null;
if (string.IsNullOrEmpty(media.ThumbnailPath)) return null;
return new MediaDto {
MimeType = "image/webp",
Path = media.ThumbnailPath
};
}
//TODO: for Previews the MimeType should be the correct one (depending on which format is used for previews and if the asset is a video)
/// <inheritdoc />
public MediaDto? GetPreviewData(Guid id, Guid? userId, EAccessLevel accessLevel = EAccessLevel.User) {
var media = GetVisibleAsset(id, userId, accessLevel);
if(media == null) return null;
if (string.IsNullOrEmpty(media.PreviewPath)) return null;
return new MediaDto {
MimeType = "image/webp",
Path = media.PreviewPath
};
}
/// <inheritdoc />
public MediaDto? GetConvertedData(Guid id, Guid? userId, EAccessLevel accessLevel = EAccessLevel.User) {
var media = GetVisibleAsset(id, userId, accessLevel);
if (media == null) return null;
if (string.IsNullOrEmpty(media.ConvertedPath) || string.IsNullOrEmpty(media.ConvertedMimeType)) return null;
return new MediaDto {
MimeType = media.ConvertedMimeType,
Path = media.ConvertedPath
};
}
/// <inheritdoc />
public void Dispose() {
context.Dispose();
personRepository.Dispose();
}
}