first commit

This commit is contained in:
Zeeshaun
2026-04-20 18:15:08 +00:00
commit c98e158e26
111 changed files with 77565 additions and 0 deletions
@@ -0,0 +1,91 @@
using Fragalytics.Web.Infrastructure;
using Fragalytics.Web.Models;
using Microsoft.Extensions.Options;
namespace Fragalytics.Web.Services;
public sealed class ClipMediaService
{
private static readonly HashSet<string> AllowedExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".mp4",
".webm"
};
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 (!AllowedExtensions.Contains(extension))
{
throw new InvalidOperationException("Only .mp4 and .webm 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) ? InferContentType(extension) : contentType,
FileSizeBytes = fileSize
};
state.ClipAssets.Add(asset);
clipJob.ClipAssetId = asset.Id;
clipJob.Status = "rendered";
clipJob.ErrorMessage = null;
clipJob.CompletedAtUtc = DateTime.UtcNow;
clipJob.Payload["playback"] = "available";
return asset.Id;
}, cancellationToken);
}
private static string InferContentType(string extension) => extension.ToLowerInvariant() switch
{
".webm" => "video/webm",
_ => "video/mp4"
};
}