Files
fragalytics/src/Fragalytics.Web/Services/ClipMediaService.cs
T
2026-04-21 15:57:09 +00:00

79 lines
2.8 KiB
C#

using Fragalytics.Web.Infrastructure;
using Fragalytics.Web.Models;
using Microsoft.Extensions.Options;
namespace Fragalytics.Web.Services;
public sealed class ClipMediaService
{
private readonly JsonAppStore _store;
private readonly FragalyticsOptions _options;
public ClipMediaService(JsonAppStore store, IOptions<FragalyticsOptions> options)
{
_store = store;
_options = options.Value;
}
public async Task<int> AttachClipAssetAsync(
int clipJobId,
string fileName,
string? contentType,
Stream input,
CancellationToken cancellationToken)
{
var extension = Path.GetExtension(fileName);
if (!ClipMediaFormats.AllowedExtensions.Contains(extension))
{
throw new InvalidOperationException($"Only {ClipMediaFormats.Mp4Extension} and {ClipMediaFormats.WebMExtension} clip uploads are supported.");
}
var clipsDirectory = Path.Combine(Path.GetFullPath(_options.DataDirectory), "clips");
Directory.CreateDirectory(clipsDirectory);
var safeFileName = Path.GetFileName(fileName);
var storedName = $"{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}_{clipJobId}{extension}";
var storedPath = Path.Combine(clipsDirectory, storedName);
long fileSize;
await using (var output = File.Create(storedPath))
{
await input.CopyToAsync(output, cancellationToken);
fileSize = output.Length;
}
return await _store.UpdateAsync(state =>
{
var clipJob = state.ClipJobs.FirstOrDefault(item => item.Id == clipJobId)
?? throw new InvalidOperationException("Clip job not found.");
if (clipJob.ClipAssetId is int existingAssetId)
{
var existingAsset = state.ClipAssets.FirstOrDefault(item => item.Id == existingAssetId);
if (existingAsset is not null && File.Exists(existingAsset.StoredPath))
{
File.Delete(existingAsset.StoredPath);
}
state.ClipAssets.RemoveAll(item => item.Id == existingAssetId);
}
var asset = new ClipAssetRecord
{
Id = state.NextClipAssetId++,
ClipJobId = clipJobId,
FileName = safeFileName,
StoredPath = storedPath,
ContentType = string.IsNullOrWhiteSpace(contentType) ? ClipMediaFormats.InferContentType(extension) : contentType,
FileSizeBytes = fileSize
};
state.ClipAssets.Add(asset);
clipJob.ClipAssetId = asset.Id;
clipJob.Status = ClipJobStatuses.Rendered;
clipJob.ErrorMessage = null;
clipJob.CompletedAtUtc = DateTime.UtcNow;
return asset.Id;
}, cancellationToken);
}
}