diff --git a/Encoder/Encoder.csproj b/Encoder/Encoder.csproj index 3ea8f06..59f3754 100644 --- a/Encoder/Encoder.csproj +++ b/Encoder/Encoder.csproj @@ -8,6 +8,7 @@ + diff --git a/Encoder/EncoderService.cs b/Encoder/EncoderService.cs new file mode 100644 index 0000000..a0b7206 --- /dev/null +++ b/Encoder/EncoderService.cs @@ -0,0 +1,39 @@ +namespace Encoder; + +public class EncoderService : IEncoderService { + Queue JobQueue; + public EncoderService(EncoderServiceOptions options) { + JobQueue = new Queue(); + } + + public Guid EnqueueJob(EncodingJob job) { + JobQueue.Enqueue(job); + return job.Id; + } + + public EncodingJob? GetJobStatus(Guid jobId) { + return JobQueue.FirstOrDefault(j => j.Id == jobId); + } + + public void RemoveJob(Guid jobId) { + var job = JobQueue.FirstOrDefault(j => j.Id == jobId); + if (job != null) { + var tempQueue = new Queue(); + while (JobQueue.Count > 0) { + var currentJob = JobQueue.Dequeue(); + if (currentJob.Id != jobId) { + tempQueue.Enqueue(currentJob); + } + } + JobQueue = tempQueue; + } + } + + public Task ProcessNextJob() { + var job = JobQueue.Dequeue(); + // Encode.... + + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Encoder/EncoderServiceOptions.cs b/Encoder/EncoderServiceOptions.cs new file mode 100644 index 0000000..930e339 --- /dev/null +++ b/Encoder/EncoderServiceOptions.cs @@ -0,0 +1,6 @@ +namespace Encoder; + +public record class EncoderServiceOptions { + public string OutputPath { get; init; } = Path.GetTempPath(); + public string FfmpegPath { get; init; } = Path.Combine(Environment.ProcessPath, "ffmpeg"); +} \ No newline at end of file diff --git a/Encoder/EncodingJob.cs b/Encoder/EncodingJob.cs new file mode 100644 index 0000000..6b1bc2f --- /dev/null +++ b/Encoder/EncodingJob.cs @@ -0,0 +1,9 @@ +namespace Encoder; + +public record EncodingJob(Guid Id) { + public JobStatus Status { get; set; } = JobStatus.Pending; + public DateTime CreatedAt { get; init; } = DateTime.Now; + public DateTime? CompletedAt { get; set; } = null; + public string OrigFilePath { get; init; } = string.Empty; + public string EncodedFilePath { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Encoder/IEncoderService.cs b/Encoder/IEncoderService.cs new file mode 100644 index 0000000..984a003 --- /dev/null +++ b/Encoder/IEncoderService.cs @@ -0,0 +1,7 @@ +namespace Encoder; + +public interface IEncoderService { + public Guid EnqueueJob(EncodingJob job); + public EncodingJob? GetJobStatus(Guid jobId); + public void RemoveJob(Guid jobId); +} \ No newline at end of file diff --git a/Encoder/JobStatus.cs b/Encoder/JobStatus.cs new file mode 100644 index 0000000..f82bd79 --- /dev/null +++ b/Encoder/JobStatus.cs @@ -0,0 +1,8 @@ +namespace Encoder; + +public enum JobStatus { + Pending, + InProgress, + Completed, + Failed +} \ No newline at end of file diff --git a/Encoder/Program.cs b/Encoder/Program.cs index 0ce2a1c..0e2b02e 100644 --- a/Encoder/Program.cs +++ b/Encoder/Program.cs @@ -1,14 +1,16 @@ -using System.Collections; -using Microsoft.AspNetCore.Http.HttpResults; -Queue JobQueue = new(); +using Encoder; var builder = WebApplication.CreateBuilder(args); //Settings -string tmpFilePath = builder.Configuration.GetValue("TempFilePath") ?? Path.GetTempPath(); - +string? tmpFilePath = builder.Configuration.GetValue("TempFilePath"); +string? ffmpegPath = builder.Configuration.GetValue("FfmpegPath"); //Services builder.Services.AddOpenApi(); + +var encoderOptions = new EncoderServiceOptions { OutputPath = tmpFilePath }; +builder.Services.AddSingleton(_ => new (encoderOptions)); + var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } @@ -18,7 +20,7 @@ app.UseHttpsRedirection(); // Get a video file as multipart form data and schedule it for encoding // Get video encoding settings from query parameters // Returns the ID of the job handling the encoding -app.MapPost("encode", context => +app.MapPost("encode", context => { var request = context.Request; if (!request.HasFormContentType) { @@ -34,16 +36,25 @@ app.MapPost("encode", context => return context.Response.WriteAsync("No video file provided."); } - var Job = new EncodingJob(Guid.NewGuid()); - JobQueue.Enqueue(Job); - - return context.Response.WriteAsJsonAsync(new { JobId = Job.Id }); + var job = new EncodingJob(Guid.NewGuid()); + var encSrv = context.RequestServices.GetService(); + if (encSrv != null) encSrv.EnqueueJob(job); + else { + context.Response.StatusCode = 500; + return context.Response.WriteAsync("Encoder service not available."); + } + + context.Response.StatusCode = 200; + return context.Response.WriteAsJsonAsync(new { JobId = job.Id }); }); // Check the status of an encoding job by its ID -app.MapGet("status/{jobId:guid}", (Guid jobId) => +app.MapGet("status/{jobId:guid}", (HttpContext context, Guid jobId) => { - var job = JobQueue.FirstOrDefault(j => j.Id == jobId); + var encSrv = context.RequestServices.GetService(); + if (encSrv == null) return Results.InternalServerError(); + + var job = encSrv.GetJobStatus(jobId); if (job == null) { return Results.NotFound(new { Message = "Job not found." }); } @@ -51,44 +62,26 @@ app.MapGet("status/{jobId:guid}", (Guid jobId) => return Results.Ok(new { JobId = job.Id, Status = job.Status.ToString(), - CreatedAt = job.CreatedAt, - CompletedAt = job.CompletedAt + job.CreatedAt, + job.CompletedAt }); }); -app.MapGet("file/{jobId:guid}", (Guid jobId) => +app.MapGet("file/{jobId:guid}", (HttpContext context, Guid jobId) => { - var job = JobQueue.FirstOrDefault(j => j.Id == jobId); - if (job == null) { - return Results.NotFound(new { Message = "Job not found." }); - } + var encSrv = context.RequestServices.GetService(); + if (encSrv == null) return Results.InternalServerError(); + + var job = encSrv.GetJobStatus(jobId); + if (job == null) return Results.NotFound(new { Message = "Job not found." }); - if (job.Status != JobStatus.Completed) { - return Results.BadRequest(new { Message = "Job is not completed yet." }); - } + if (job.Status != JobStatus.Completed) return Results.BadRequest(new { Message = "Job is not completed yet." }); var filePath = job.EncodedFilePath; - if (!File.Exists(filePath)) { - return Results.NotFound(new { Message = "Encoded file not found." }); - } + if (!File.Exists(filePath)) return Results.NotFound(new { Message = "Encoded file not found." }); var fileBytes = File.ReadAllBytes(filePath); return Results.File(fileBytes, "video/mp4", Path.GetFileName(filePath), enableRangeProcessing:true); }); -app.Run(); - -public enum JobStatus { - Pending, - InProgress, - Completed, - Failed -} - -public record EncodingJob(Guid Id) { - public JobStatus Status { get; set; } = JobStatus.Pending; - public DateTime CreatedAt { get; init; } = DateTime.Now; - public DateTime? CompletedAt { get; set; } = null; - public string OrigFilePath { get; init; } = string.Empty; - public string EncodedFilePath { get; set; } = string.Empty; -} \ No newline at end of file +app.Run(); \ No newline at end of file diff --git a/Encoder/appsettings.json b/Encoder/appsettings.json index b2d2fe8..6fef078 100644 --- a/Encoder/appsettings.json +++ b/Encoder/appsettings.json @@ -6,5 +6,6 @@ } }, "AllowedHosts": "*", - "TemporaryFilesPath": "./Temp" + "TemporaryFilesPath": "./Temp", + "FfmpegPath": "./ffmpeg" }