public TranscodingJobState SaveProgress(int id, bool failed, bool done, TimeSpan progress, TimeSpan?verifyProgress, string machineName) { InsertClientHeartbeat(machineName); TranscodingJobState jobState = failed ? TranscodingJobState.Failed : done ? TranscodingJobState.Done : TranscodingJobState.InProgress; using (var connection = Helper.GetConnection()) { connection.Open(); // Only allow progress updates for tasks which have statetaskState = InProgress // This will prevent out-of-order updates causing tasks set to either Failed or Done // to be set back to InProgress int updatedRows = connection.Execute( "UPDATE FfmpegTasks SET Progress = @Progress, VerifyProgress = @VerifyProgress, Heartbeat = @Heartbeat, TaskState = @State, HeartbeatMachineName = @MachineName WHERE Id = @Id" + " AND TaskState = @InProgressState;", new { Id = id, Progress = progress.TotalSeconds, VerifyProgress = verifyProgress?.TotalSeconds, Heartbeat = DateTimeOffset.UtcNow.UtcDateTime, State = jobState, InProgressState = TranscodingJobState.InProgress, machineName }); if (updatedRows != 1) { throw new Exception($"Failed to update progress for task id {id}"); } } return(jobState); }
private static void UpdateVideoJob(BaseJob job, IDbConnection connection) { JobRequest jobRequest = connection.Query <JobRequest>( "SELECT JobCorrelationId, VideoSourceFilename, AudioSourceFilename, DestinationFilename, Needed, EnableDash, EnablePsnr FROM FfmpegVideoRequest WHERE JobCorrelationId = @Id", new { Id = job.JobCorrelationId }) .SingleOrDefault(); if (jobRequest == null) { throw new ArgumentException($@"Job with correlation id {job.JobCorrelationId} not found"); } jobRequest.Targets = connection.Query <DestinationFormat>( "SELECT JobCorrelationId, Width, Height, VideoBitrate, AudioBitrate FROM FfmpegVideoRequestTargets WHERE JobCorrelationId = @Id;", new { Id = job.JobCorrelationId }) .ToArray(); using (var scope = TransactionUtils.CreateTransactionScope()) { Type jobType = job.GetType(); TranscodingJobState jobState = job.Failed ? TranscodingJobState.Failed : job.Done ? TranscodingJobState.Done : TranscodingJobState.InProgress; if (jobType == typeof(VideoTranscodingJob)) { int updatedRows = connection.Execute( "UPDATE FfmpegVideoJobs SET Progress = @Progress, Heartbeat = @Heartbeat, State = @State, HeartbeatMachineName = @MachineName WHERE Id = @Id;", new { Id = job.Id, Progress = job.Progress.TotalSeconds, Heartbeat = DateTimeOffset.UtcNow.UtcDateTime, State = jobState, MachineName = job.MachineName, }); if (updatedRows != 1) { throw new Exception($"Failed to update progress for job id {job.Id}"); } if (jobRequest.EnablePsnr) { foreach (FfmpegPart chunk in ((VideoTranscodingJob)job).Chunks) { connection.Execute( "UPDATE FfmpegVideoParts SET PSNR = @Psnr WHERE Id = @Id;", new { Id = chunk.Id, Psnr = chunk.Psnr }); } } } else if (jobType == typeof(MergeJob)) { int updatedRows = connection.Execute( "UPDATE FfmpegMergeJobs SET Progress = @Progress, Heartbeat = @Heartbeat, State = @State, HeartbeatMachineName = @MachineName WHERE Id = @Id;", new { Id = job.Id, Progress = job.Progress.TotalSeconds, Heartbeat = DateTimeOffset.UtcNow.UtcDateTime, State = jobState, MachineName = job.MachineName }); if (updatedRows != 1) { throw new Exception($"Failed to update progress for job id {job.Id}"); } var states = connection.Query <string>( "SELECT State FROM FfmpegMergeJobs WHERE JobCorrelationId = @Id;", new { Id = job.JobCorrelationId }) .Select(value => Enum.Parse(typeof(TranscodingJobState), value)) .Cast <TranscodingJobState>(); if (states.All(x => x == TranscodingJobState.Done)) { string tempFolder = String.Concat(Path.GetDirectoryName(jobRequest.DestinationFilename), Path.DirectorySeparatorChar, jobRequest.JobCorrelationId.ToString("N")); Directory.Delete(tempFolder, true); } } else if (jobType == typeof(Mp4boxJob)) { int updatedRows = connection.Execute( "UPDATE Mp4boxJobs SET Heartbeat = @Heartbeat, State = @State, HeartbeatMachineName = @MachineName WHERE JobCorrelationId = @Id;", new { Id = job.JobCorrelationId, Progress = job.Progress.TotalSeconds, Heartbeat = DateTimeOffset.UtcNow.UtcDateTime, State = jobState, MachineName = job.MachineName }); if (updatedRows != 1) { throw new Exception($"Failed to update progress for job id {job.Id}"); } } scope.Complete(); } using (var scope = TransactionUtils.CreateTransactionScope()) { ICollection <TranscodingJobState> totalJobs = connection.Query <TranscodingJobState>( "SELECT State FROM FfmpegVideoJobs WHERE JobCorrelationId = @Id;", new { Id = jobRequest.JobCorrelationId }) .ToList(); if (totalJobs.Any(x => x != TranscodingJobState.Done)) { // Not all transcoding jobs are finished return; } totalJobs = connection.Query <TranscodingJobState>( "SELECT State FROM FfmpegMergeJobs WHERE JobCorrelationId = @Id;", new { Id = jobRequest.JobCorrelationId }) .ToList(); if (totalJobs.Any(x => x != TranscodingJobState.Done)) { // Not all merge jobs are finished return; } string destinationFilename = jobRequest.DestinationFilename; string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(destinationFilename); string fileExtension = Path.GetExtension(destinationFilename); string outputFolder = Path.GetDirectoryName(destinationFilename); if (totalJobs.Count == 0) { QueueMergeJob(job, connection, outputFolder, fileNameWithoutExtension, fileExtension, jobRequest); } else if (jobRequest.EnableDash) { QueueMpegDashMergeJob(job, destinationFilename, connection, jobRequest, fileNameWithoutExtension, outputFolder, fileExtension); } scope.Complete(); } }