public async Task ExecuteAsync(WorkerJobHelper <AIJob> jobHelper) { S3Locator inputFile; if (!jobHelper.JobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile)) { throw new Exception("Invalid or missing input file."); } string mediaFileUrl; if (!string.IsNullOrWhiteSpace(inputFile.HttpEndpoint)) { mediaFileUrl = inputFile.HttpEndpoint; } else { var bucketLocation = await inputFile.GetBucketLocationAsync(); var s3SubDomain = !string.IsNullOrWhiteSpace(bucketLocation) ? $"s3-{bucketLocation}" : "s3"; mediaFileUrl = $"https://{s3SubDomain}.amazonaws.com/{inputFile.AwsS3Bucket}/{inputFile.AwsS3Key}"; } string mediaFormat; if (mediaFileUrl.EndsWith("mp3", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "mp3"; } else if (mediaFileUrl.EndsWith("mp4", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "mp4"; } else if (mediaFileUrl.EndsWith("wav", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "wav"; } else if (mediaFileUrl.EndsWith("flac", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "flac"; } else { throw new Exception($"Unable to determine media format from input file '{mediaFileUrl}'"); } var transcribeParameters = new StartTranscriptionJobRequest { TranscriptionJobName = "TranscriptionJob-" + jobHelper.JobAssignmentId.Substring(jobHelper.JobAssignmentId.LastIndexOf("/") + 1), LanguageCode = "en-US", Media = new Media { MediaFileUri = mediaFileUrl }, MediaFormat = mediaFormat, OutputBucketName = jobHelper.Request.GetRequiredContextVariable("ServiceOutputBucket") }; using (var transcribeService = new AmazonTranscribeServiceClient()) await transcribeService.StartTranscriptionJobAsync(transcribeParameters); }
public async Task ExecuteAsync(WorkerJobHelper <TransformJob> job) { S3Locator inputFile; if (!job.JobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile)) { throw new Exception("Invalid or missing input file."); } S3Locator outputLocation; if (!job.JobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } if (string.IsNullOrWhiteSpace(inputFile.AwsS3Bucket) || string.IsNullOrWhiteSpace(inputFile.AwsS3Key)) { throw new Exception("Not able to obtain input file"); } var data = await inputFile.GetAsync(); var localFileName = "/tmp/" + Guid.NewGuid(); await data.WriteResponseStreamToFileAsync(localFileName, true, CancellationToken.None); var tempFileName = "/tmp/" + Guid.NewGuid() + ".mp4"; var ffmpegParams = new[] { "-y", "-i", localFileName, "-preset", "ultrafast", "-vf", "scale=-1:360", "-c:v", "libx264", "-pix_fmt", "yuv420p", tempFileName }; var ffmpegProcess = await FFmpegProcess.RunAsync(ffmpegParams); File.Delete(localFileName); var s3Params = new PutObjectRequest { BucketName = outputLocation.AwsS3Bucket, Key = (outputLocation.AwsS3KeyPrefix ?? string.Empty) + Guid.NewGuid().ToString() + ".mp4", FilePath = tempFileName, ContentType = "video/mp4" }; var outputS3 = await outputLocation.GetClientAsync(); var putResp = await outputS3.PutObjectAsync(s3Params); job.JobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Params.BucketName, AwsS3Key = s3Params.Key }; await job.CompleteAsync(); }
public async Task ExecuteAsync(WorkerJobHelper <WorkflowJob> job) { using (var stepFunctionClient = new AmazonStepFunctionsClient()) await stepFunctionClient.StartExecutionAsync( new StartExecutionRequest { Input = new { Input = job.JobInput, NotificationEndpoint = new NotificationEndpoint { HttpEndpoint = job.JobAssignmentId + "/notifications" } }.ToMcmaJson().ToString(), StateMachineArn = job.Request.GetRequiredContextVariable($"{job.Profile.Name}Id") }); }
public async Task ExecuteAsync(WorkerJobHelper <TransformJob> job) { var ec2hostname = job.Request.GetRequiredContextVariable("HostnameInstanceEC2"); var ec2Url = "http://" + ec2hostname + "/new-transform-job"; var message = new { input = job.JobInput, notificationEndpoint = new NotificationEndpoint { HttpEndpoint = job.JobAssignmentId + "/notifications" } }; Logger.Debug("Sending to", ec2Url, "message", message); var mcmaHttp = new McmaHttpClient(); await mcmaHttp.PostAsJsonAsync(ec2Url, message); Logger.Debug("Done"); }
public async Task ExecuteAsync(WorkerJobHelper <AIJob> jobHelper) { S3Locator inputFile; if (!jobHelper.JobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile)) { throw new Exception("Invalid or missing input file."); } var randomBytes = new byte[16]; new Random().NextBytes(randomBytes); var clientToken = randomBytes.HexEncode(); var base64JobId = Encoding.UTF8.GetBytes(jobHelper.JobAssignmentId).HexEncode(); var rekoParams = new StartCelebrityRecognitionRequest { Video = new Video { S3Object = new Amazon.Rekognition.Model.S3Object { Bucket = inputFile.AwsS3Bucket, Name = inputFile.AwsS3Key } }, ClientRequestToken = clientToken, JobTag = base64JobId, NotificationChannel = new NotificationChannel { RoleArn = Environment.GetEnvironmentVariable("REKO_SNS_ROLE_ARN"), SNSTopicArn = Environment.GetEnvironmentVariable("SNS_TOPIC_ARN") } }; using (var rekognitionClient = new AmazonRekognitionClient()) await rekognitionClient.StartCelebrityRecognitionAsync(rekoParams); }
public async Task ExecuteAsync(WorkerJobHelper <AmeJob> job) { S3Locator inputFile; if (!job.JobInput.TryGet(nameof(inputFile), out inputFile)) { throw new Exception("Unable to parse input file as S3Locator"); } S3Locator outputLocation; if (!job.JobInput.TryGet(nameof(outputLocation), out outputLocation)) { throw new Exception("Unable to parse output location as S3Locator"); } MediaInfoProcess mediaInfoProcess; if (inputFile is HttpEndpointLocator httpEndpointLocator && !string.IsNullOrWhiteSpace(httpEndpointLocator.HttpEndpoint)) { Logger.Debug("Running MediaInfo against " + httpEndpointLocator.HttpEndpoint); mediaInfoProcess = await MediaInfoProcess.RunAsync("--Output=EBUCore_JSON", httpEndpointLocator.HttpEndpoint); }
public async Task ExecuteAsync(WorkerJobHelper <AIJob> jobHelper) { S3Locator inputFile; if (!jobHelper.JobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile)) { throw new Exception($"Invalid or missing input file"); } string mediaFileUrl; if (!string.IsNullOrWhiteSpace(inputFile.HttpEndpoint)) { mediaFileUrl = inputFile.HttpEndpoint; } else { var bucketLocation = await inputFile.GetBucketLocationAsync(); var s3SubDomain = !string.IsNullOrWhiteSpace(bucketLocation) ? $"s3-{bucketLocation}" : "s3"; mediaFileUrl = $"https://{s3SubDomain}.amazonaws.com/{inputFile.AwsS3Bucket}/{inputFile.AwsS3Key}"; } var authTokenUrl = jobHelper.Request.ApiUrl() + "/auth/" + jobHelper.Request.Location() + "/Accounts/" + jobHelper.Request.AccountID() + "/AccessToken?allowEdit=true"; var customHeaders = new Dictionary <string, string> { ["Ocp-Apim-Subscription-Key"] = jobHelper.Request.SubscriptionKey() }; Logger.Debug($"Generate Azure Video Indexer Token: Doing a GET on {authTokenUrl}"); var mcmaHttp = new McmaHttpClient(); var response = await mcmaHttp.GetAsync(authTokenUrl, headers : customHeaders).WithErrorHandling(); var apiToken = await response.Content.ReadAsJsonAsync(); Logger.Debug($"Azure API Token: {apiToken}"); // call the Azure API to process the video // in this scenario the video is located in a public link // so no need to upload the file to Azure /* Sample URL Structure * https://api.videoindexer.ai/{location}/Accounts/{accountId}/Videos? * accessToken={accessToken}& * name={name}?description={string}& * partition={string}& * externalId={string}& * callbackUrl={string}& * metadata={string}& * language={string}& * videoUrl={string}& * fileName={string}& * indexingPreset={string}& * streamingPreset=Default& * linguisticModelId={string}& * privacy={string}& * externalUrl={string}" */ var secureHost = new Uri(jobHelper.JobAssignmentId, UriKind.Absolute).Host; var nonSecureHost = new Uri(jobHelper.Request.GetRequiredContextVariable("PublicUrlNonSecure"), UriKind.Absolute).Host; var callbackUrl = Uri.EscapeDataString(jobHelper.JobAssignmentId.Replace(secureHost, nonSecureHost) + "/notifications"); var postVideoUrl = jobHelper.Request.ApiUrl() + "/" + jobHelper.Request.Location() + "/Accounts/" + jobHelper.Request.AccountID() + "/Videos?accessToken=" + apiToken + "&name=" + inputFile.AwsS3Key + "&callbackUrl=" + callbackUrl + "&videoUrl=" + mediaFileUrl + "&fileName=" + inputFile.AwsS3Key; Logger.Debug($"Call Azure Video Indexer API: Doing a POST on {postVideoUrl}"); var postVideoResponse = await mcmaHttp.PostAsync(postVideoUrl, null, customHeaders).WithErrorHandling(); var azureAssetInfo = await postVideoResponse.Content.ReadAsJsonAsync(); Logger.Debug("azureAssetInfo: ", azureAssetInfo); try { jobHelper.JobOutput["jobInfo"] = azureAssetInfo; await jobHelper.UpdateJobAssignmentOutputAsync(); } catch (Exception error) { Logger.Error("Error updating the job", error); } }
protected override async Task ExecuteAsync(WorkerRequest request, ProcessRekognitionResult requestInput) { var workerJobHelper = new WorkerJobHelper <AIJob>( DbTableProvider.Table(request.TableName()), ResourceManagerProvider.GetResourceManager(request), request, requestInput.JobAssignmentId); try { await workerJobHelper.InitializeAsync(); var jobInput = workerJobHelper.JobInput; S3Locator outputLocation; if (!jobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var s3Bucket = outputLocation.AwsS3Bucket; var rekoJobId = requestInput.JobInfo.RekoJobId; var rekoJobType = requestInput.JobInfo.RekoJobType; var status = requestInput.JobInfo.Status; if (status != "SUCCEEDED") { throw new Exception($"AI Rekognition failed job info: rekognition status:" + status); } object data = null; switch (rekoJobType) { case "StartCelebrityRecognition": using (var rekognitionClient = new AmazonRekognitionClient()) data = await rekognitionClient.GetCelebrityRecognitionAsync(new GetCelebrityRecognitionRequest { JobId = rekoJobId, /* required */ MaxResults = 1000000, SortBy = "TIMESTAMP" }); break; case "StartLabelDetection": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); case "StartContentModeration": throw new NotImplementedException("StartContentModeration has not yet been implemented"); case "StartPersonTracking": throw new NotImplementedException("StartPersonTracking has not yet been implemented"); case "StartFaceDetection": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); case "StartFaceSearch": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); default: throw new Exception($"Unknown job type '{rekoJobType}'"); } if (data == null) { throw new Exception($"No data was returned by AWS Rekognition"); } var newS3Key = $"reko_{Guid.NewGuid()}.json"; var s3Params = new PutObjectRequest { BucketName = outputLocation.AwsS3Bucket, Key = newS3Key, ContentBody = data.ToMcmaJson().ToString(), ContentType = "application/json" }; try { var destS3 = await outputLocation.GetClientAsync(); await destS3.PutObjectAsync(s3Params); } catch (Exception error) { Logger.Error("Unable to write output file to bucket '" + s3Bucket + "' with key '" + newS3Key + "'"); Logger.Exception(error); } workerJobHelper.JobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Bucket, AwsS3Key = newS3Key }; await workerJobHelper.CompleteAsync(); } catch (Exception ex) { Logger.Exception(ex); try { await workerJobHelper.FailAsync(ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } }
public Task ExecuteAsync(WorkerJobHelper <AIJob> job) => throw new NotImplementedException($"{Name} profile has not yet been implemented for Azure.");
protected override async Task ExecuteAsync(WorkerRequest request, ProcessNotificationRequest requestInput) { var azureVideoId = requestInput.Notification?.Id; var azureState = requestInput.Notification?.State; if (azureVideoId == null || azureState == null) { Logger.Warn("POST is not coming from Azure Video Indexer. Expected notification to have id and state properties."); return; } var workerJobHelper = new WorkerJobHelper<AIJob>( DbTableProvider.Table(request.TableName()), ResourceManagerProvider.GetResourceManager(request), request, requestInput.JobAssignmentId); try { await workerJobHelper.InitializeAsync(); var authTokenUrl = request.ApiUrl() + "/auth/" + request.Location() + "/Accounts/" + request.AccountID() + "/AccessToken?allowEdit=true"; var customHeaders = new Dictionary<string, string> { ["Ocp-Apim-Subscription-Key"] = request.SubscriptionKey() }; Logger.Debug($"Generate Azure Video Indexer Token: Doing a GET on {authTokenUrl}"); var mcmaHttp = new McmaHttpClient(); var response = await mcmaHttp.GetAsync(authTokenUrl, headers: customHeaders).WithErrorHandling(); var apiToken = await response.Content.ReadAsJsonAsync(); Logger.Debug($"Azure API Token: {apiToken}"); var metadataFromAzureVideoIndexer = $"{request.ApiUrl()}/{request.Location()}/Accounts/{request.AccountID()}/Videos/{azureVideoId}/Index?accessToken={apiToken}&language=English"; Logger.Debug($"Getting Azure video metadata from: {metadataFromAzureVideoIndexer}"); var indexedVideoMetadataResponse = await mcmaHttp.GetAsync(metadataFromAzureVideoIndexer, headers: customHeaders).WithErrorHandling(); var videoMetadata = await indexedVideoMetadataResponse.Content.ReadAsJsonAsync(); Logger.Debug($"Azure AI video metadata: {videoMetadata}"); S3Locator outputLocation; if (!workerJobHelper.JobInput.TryGet<S3Locator>(nameof(outputLocation), out outputLocation)) throw new Exception("Invalid or missing output location."); var jobOutputBucket = outputLocation.AwsS3Bucket; var jobOutputKeyPrefix = outputLocation.AwsS3KeyPrefix != null ? outputLocation.AwsS3KeyPrefix : ""; // get the info about the destination bucket to store the result of the job var s3Params = new PutObjectRequest { BucketName = jobOutputBucket, Key = jobOutputKeyPrefix + azureVideoId + "-" + Guid.NewGuid() + ".json", ContentBody = videoMetadata.ToString(), ContentType = "application/json" }; var destS3 = await outputLocation.GetClientAsync(); await destS3.PutObjectAsync(s3Params); //updating JobAssignment with jobOutput workerJobHelper.JobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Params.BucketName, AwsS3Key = s3Params.Key }; await workerJobHelper.CompleteAsync(); } catch (Exception ex) { Logger.Exception(ex); try { await workerJobHelper.FailAsync(ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } }
public async Task ExecuteAsync(WorkerJobHelper <AIJob> jobHelper) { S3Locator inputFile; if (!jobHelper.JobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile)) { throw new Exception("Invalid or missing input file."); } S3Locator outputLocation; if (!jobHelper.JobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var s3Bucket = inputFile.AwsS3Bucket; var s3Key = inputFile.AwsS3Key; GetObjectResponse s3Object; try { s3Object = await inputFile.GetAsync(); } catch (Exception error) { throw new Exception($"Unable to read file in bucket '{s3Bucket}' with key '{s3Key}'.", error); } var inputText = await new StreamReader(s3Object.ResponseStream).ReadToEndAsync(); var translateParameters = new TranslateTextRequest { SourceLanguageCode = jobHelper.JobInput.TryGet("sourceLanguageCode", out string srcLanguageCode) ? srcLanguageCode : "auto", TargetLanguageCode = jobHelper.JobInput.Get <string>("targetLanguageCode"), Text = inputText }; var translateService = new AmazonTranslateClient(); var translateResponse = await translateService.TranslateTextAsync(translateParameters); var s3Params = new PutObjectRequest { BucketName = outputLocation.AwsS3Bucket, Key = (!string.IsNullOrWhiteSpace(outputLocation.AwsS3KeyPrefix) ? outputLocation.AwsS3Key : string.Empty) + Guid.NewGuid() + ".txt", ContentBody = translateResponse.TranslatedText }; var outputS3 = await outputLocation.GetClientAsync(); await outputS3.PutObjectAsync(s3Params); jobHelper.JobOutput.Set("outputFile", new S3Locator { AwsS3Bucket = s3Params.BucketName, AwsS3Key = s3Params.Key }); await jobHelper.CompleteAsync(); } }
protected override async Task ExecuteAsync(WorkerRequest request, ProcessTranscribeJobResult requestInput) { var workerJobHelper = new WorkerJobHelper <AIJob>( DbTableProvider.Table(request.TableName()), ResourceManagerProvider.GetResourceManager(request), request, requestInput.JobAssignmentId); try { await workerJobHelper.InitializeAsync(); S3Locator outputLocation; if (!workerJobHelper.JobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var copySource = Uri.EscapeDataString(requestInput.OutputFile.AwsS3Bucket + "/" + requestInput.OutputFile.AwsS3Key); var s3Bucket = outputLocation.AwsS3Bucket; var s3Key = (!string.IsNullOrWhiteSpace(outputLocation.AwsS3KeyPrefix) ? outputLocation.AwsS3KeyPrefix : string.Empty) + requestInput.OutputFile.AwsS3Key; try { var destS3 = await outputLocation.GetClientAsync(); await destS3.CopyObjectAsync(new CopyObjectRequest { SourceBucket = requestInput.OutputFile.AwsS3Bucket, SourceKey = requestInput.OutputFile.AwsS3Key, DestinationBucket = s3Bucket, DestinationKey = s3Key }); } catch (Exception error) { throw new Exception("Unable to copy output file to bucket '" + s3Bucket + "' with key'" + s3Key + "'", error); } workerJobHelper.JobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Bucket, AwsS3Key = s3Key }; await workerJobHelper.CompleteAsync(); } catch (Exception ex) { Logger.Exception(ex); try { await workerJobHelper.FailAsync(ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } // Cleanup: Deleting original output file try { var sourceS3 = await requestInput.OutputFile.GetClientAsync(); await sourceS3.DeleteObjectAsync(new DeleteObjectRequest { BucketName = requestInput.OutputFile.AwsS3Bucket, Key = requestInput.OutputFile.AwsS3Key, }); } catch (Exception error) { Logger.Error("Failed to cleanup transcribe output file."); Logger.Exception(error); } }