This commit is contained in:
2023-12-08 14:50:02 +00:00
parent cef1b845bc
commit 865747fbae
13 changed files with 676 additions and 6 deletions

View File

@ -0,0 +1,34 @@
using FFMpegCore;
using NostrStreamer.Database;
namespace NostrStreamer.Services.Clips;
public class ClipGenerator
{
private readonly ILogger<ClipGenerator> _logger;
private readonly Config _config;
public ClipGenerator(ILogger<ClipGenerator> logger, Config config)
{
_logger = logger;
_config = config;
}
public async Task<string> GenerateClip(UserStream stream)
{
const int clipLength = 20;
var path = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");
var cmd = FFMpegArguments
.FromUrlInput(new Uri(_config.DataHost, $"stream/{stream.Id}.m3u8"),
inOpt =>
{
inOpt.WithCustomArgument($"-ss -{clipLength}");
})
.OutputToFile(path, true, o => { o.WithDuration(TimeSpan.FromSeconds(clipLength)); })
.CancellableThrough(new CancellationTokenSource(TimeSpan.FromSeconds(60)).Token);
_logger.LogInformation("Running command {cmd}", cmd.Arguments);
await cmd.ProcessAsynchronously();
return path;
}
}

View File

@ -0,0 +1,8 @@
namespace NostrStreamer.Services.Clips;
public interface IClipService
{
Task<ClipResult?> CreateClip(Guid streamId, string takenBy);
}
public record ClipResult(Uri Url);

View File

@ -0,0 +1,80 @@
using Amazon.S3;
using Microsoft.EntityFrameworkCore;
using NostrStreamer.Database;
namespace NostrStreamer.Services.Clips;
public class S3ClipService : IClipService
{
private readonly ClipGenerator _generator;
private readonly AmazonS3Client _client;
private readonly Config _config;
private readonly StreamerContext _context;
public S3ClipService(ClipGenerator generator, Config config, StreamerContext context)
{
_generator = generator;
_client = config.S3Store.CreateClient();
;
_config = config;
_context = context;
}
public async Task<ClipResult?> CreateClip(Guid streamId, string takenBy)
{
var stream = await _context.Streams
.Include(a => a.User)
.Include(a => a.Endpoint)
.FirstOrDefaultAsync(a => a.Id == streamId);
if (stream == default)
{
return default;
}
var tmpClip = await _generator.GenerateClip(stream);
var clipId = Guid.NewGuid();
var s3Path = $"{stream.Id}/clips/{clipId}.mp4";
await using var fs = new FileStream(tmpClip, FileMode.Open, FileAccess.Read);
await _client.PutObjectAsync(new()
{
BucketName = _config.S3Store.BucketName,
Key = s3Path,
InputStream = fs,
AutoCloseStream = false,
AutoResetStreamPosition = false,
ContentType = "video/mp4",
DisablePayloadSigning = _config.S3Store.DisablePayloadSigning
});
var uri = _client.GetPreSignedURL(new()
{
BucketName = _config.S3Store.BucketName,
Key = s3Path,
Expires = DateTime.UtcNow.AddYears(1000)
});
var ub = new UriBuilder(uri)
{
Scheme = _config.S3Store.PublicHost.Scheme,
Host = _config.S3Store.PublicHost.Host,
Port = _config.S3Store.PublicHost.Port
};
var clipObj = new UserStreamClip()
{
Id = clipId,
UserStreamId = stream.Id,
TakenByPubkey = takenBy,
Url = ub.Uri.ToString()
};
_context.Clips.Add(clipObj);
await _context.SaveChangesAsync();
return new(ub.Uri);
}
}

View File

@ -41,7 +41,8 @@ public class StreamEventBuilder
new("status", status),
new("p", user.PubKey, "", "host"),
new("relays", _config.Relays),
new("starts", new DateTimeOffset(stream.Starts).ToUnixTimeSeconds().ToString())
new("starts", new DateTimeOffset(stream.Starts).ToUnixTimeSeconds().ToString()),
new("service", new Uri(_config.ApiHost, "/api/nostr").ToString())
};
if (status == "live")