/*
* Copyright 2014 Amazon Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://aws.amazon.com/apache2.0
*
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and
* limitations under the License.
*/
package com.amediamanager.scheduled;
import java.io.IOException;
import org.joda.time.Instant;
import org.joda.time.Minutes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.model.DeleteMessageRequest;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageResult;
import com.amediamanager.config.ConfigurationSettings;
import com.amediamanager.config.ConfigurationSettings.ConfigProps;
import com.amediamanager.domain.Video;
import com.amediamanager.service.VideoService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Holds scheduled tasks related to our Elastic Transcoder
*
*/
public class ElasticTranscoderTasks {
protected static final Logger LOG = LoggerFactory.getLogger(ElasticTranscoderTasks.class);
@Autowired
protected ConfigurationSettings config;
@Autowired
protected VideoService videoService;
@Autowired
protected AmazonSQS sqsClient;
protected final ObjectMapper mapper = new ObjectMapper();
protected void checkStatus() {
String sqsQueue = config.getProperty(ConfigProps.TRANSCODE_QUEUE);
LOG.info("Polling transcode queue {} for changes.", sqsQueue);
ReceiveMessageRequest request = new ReceiveMessageRequest(sqsQueue)
.withMaxNumberOfMessages(3)
.withWaitTimeSeconds(20);
ReceiveMessageResult result = sqsClient.receiveMessage(request);
for (Message msg : result.getMessages()) {
handleMessage(msg);
}
LOG.info("Finished polling transcode queue {} and handled {} message(s).", sqsQueue, result.getMessages().size());
}
protected void deleteMessage(final Message message) {
DeleteMessageRequest request = new DeleteMessageRequest()
.withQueueUrl(config.getProperty(ConfigProps.TRANSCODE_QUEUE))
.withReceiptHandle(message.getReceiptHandle());
sqsClient.deleteMessage(request);
}
protected void handleMessage(final Message message) {
try {
LOG.info("Handling message received from checkStatus");
ObjectNode snsMessage = (ObjectNode) mapper.readTree(message.getBody());
ObjectNode notification = (ObjectNode) mapper.readTree(snsMessage.get("Message").asText());
String state = notification.get("state").asText();
String jobId = notification.get("jobId").asText();
String pipelineId = notification.get("pipelineId").asText();
Video video = videoService.findByTranscodeJobId(jobId);
if (video == null) {
LOG.warn("Unable to process result for job {} because it does not exist.", jobId);
Instant msgTime = Instant.parse(snsMessage.get("Timestamp").asText());
if (Minutes.minutesBetween(msgTime, new Instant()).getMinutes() > 20) {
LOG.error("Job {} has not been found for over 20 minutes, deleting message from queue", jobId);
deleteMessage(message);
}
// Leave it on the queue for now.
return;
}
if ("ERROR".equals(state)) {
LOG.warn("Job {} for pipeline {} failed to complete. Body: \n{}", jobId, pipelineId, notification.get("messageDetails").asText());
video.setThumbnailKey(videoService.getDefaultVideoPosterKey());
videoService.save(video);
} else {
// Construct our url prefix: https://bucketname.s3.amazonaws.com/output/key/
String prefix = notification.get("outputKeyPrefix").asText();
if (!prefix.endsWith("/")) { prefix += "/"; }
ObjectNode output = ((ObjectNode) ((ArrayNode) notification.get("outputs")).get(0));
String previewFilename = prefix + output.get("key").asText();
String thumbnailFilename = prefix + output.get("thumbnailPattern").asText().replaceAll("\\{count\\}", "00002") + ".png";
video.setPreviewKey(previewFilename);
video.setThumbnailKey(thumbnailFilename);
videoService.save(video);
}
deleteMessage(message);
} catch (JsonProcessingException e) {
LOG.error("JSON exception handling notification: {}", message.getBody(), e);
} catch (IOException e) {
LOG.error("IOException handling notification: {}", message.getBody(), e);
}
}
}