Some code simplifications & logging for Zencoder notifications.
This commit is contained in:
parent
d7ee2121d9
commit
fd5bcaec52
@ -1,58 +1,65 @@
|
||||
import logging
|
||||
|
||||
from bson import ObjectId
|
||||
from eve.methods.put import put_internal
|
||||
from flask import Blueprint
|
||||
from flask import abort
|
||||
from flask import request
|
||||
from application import app
|
||||
from application import utils
|
||||
|
||||
encoding = Blueprint('encoding', __name__)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@encoding.route('/zencoder/notifications', methods=['POST'])
|
||||
def zencoder_notifications():
|
||||
if app.config['ENCODING_BACKEND'] == 'zencoder':
|
||||
if not app.config['DEBUG']:
|
||||
# If we are in production, look for the Zencoder header secret
|
||||
try:
|
||||
notification_secret_request = request.headers[
|
||||
'X-Zencoder-Notification-Secret']
|
||||
except KeyError:
|
||||
return abort(401)
|
||||
# If the header is found, check it agains the one in the config
|
||||
notification_secret = app.config['ZENCODER_NOTIFICATIONS_SECRET']
|
||||
if notification_secret_request != notification_secret:
|
||||
return abort(401)
|
||||
# Cast request data into a dict
|
||||
data = request.get_json()
|
||||
files_collection = app.data.driver.db['files']
|
||||
# Find the file object based on processing backend and job_id
|
||||
lookup = {'processing.backend': 'zencoder', 'processing.job_id': str(
|
||||
data['job']['id'])}
|
||||
f = files_collection.find_one(lookup)
|
||||
if f:
|
||||
file_id = f['_id']
|
||||
# Remove internal keys (so that we can run put internal)
|
||||
internal_fields = ['_id', '_etag', '_updated', '_created', '_status']
|
||||
for field in internal_fields:
|
||||
f.pop(field, None)
|
||||
# Update processing status
|
||||
f['processing']['status'] = data['job']['state']
|
||||
# For every variation encoded, try to update the file object
|
||||
for output in data['outputs']:
|
||||
format = output['format']
|
||||
# Change the zencoder 'mpeg4' format to 'mp4' used internally
|
||||
format = 'mp4' if format == 'mpeg4' else format
|
||||
# Find a variation matching format and resolution
|
||||
variation = next((v for v in f['variations'] if v['format'] == format \
|
||||
and v['width'] == output['width']), None)
|
||||
# If found, update with delivered file size
|
||||
# TODO: calculate md5 on the storage
|
||||
if variation:
|
||||
variation['length'] = output['file_size_in_bytes']
|
||||
|
||||
r = put_internal('files', f, **{'_id': ObjectId(file_id)})
|
||||
return ''
|
||||
else:
|
||||
return abort(404)
|
||||
else:
|
||||
if app.config['ENCODING_BACKEND'] != 'zencoder':
|
||||
log.warning('Received notification from Zencoder but app not configured for Zencoder.')
|
||||
return abort(403)
|
||||
|
||||
if not app.config['DEBUG']:
|
||||
# If we are in production, look for the Zencoder header secret
|
||||
try:
|
||||
notification_secret_request = request.headers[
|
||||
'X-Zencoder-Notification-Secret']
|
||||
except KeyError:
|
||||
log.warning('Received Zencoder notification without secret.')
|
||||
return abort(401)
|
||||
# If the header is found, check it agains the one in the config
|
||||
notification_secret = app.config['ZENCODER_NOTIFICATIONS_SECRET']
|
||||
if notification_secret_request != notification_secret:
|
||||
log.warning('Received Zencoder notification with incorrect secret.')
|
||||
return abort(401)
|
||||
|
||||
# Cast request data into a dict
|
||||
data = request.get_json()
|
||||
files_collection = app.data.driver.db['files']
|
||||
# Find the file object based on processing backend and job_id
|
||||
lookup = {'processing.backend': 'zencoder', 'processing.job_id': str(data['job']['id'])}
|
||||
f = files_collection.find_one(lookup)
|
||||
if not f:
|
||||
log.warning('Unknown Zencoder job id %r', data['job']['id'])
|
||||
return abort(404)
|
||||
|
||||
file_id = f['_id']
|
||||
# Remove internal keys (so that we can run put internal)
|
||||
f = utils.remove_private_keys(f)
|
||||
|
||||
# Update processing status
|
||||
f['processing']['status'] = data['job']['state']
|
||||
# For every variation encoded, try to update the file object
|
||||
for output in data['outputs']:
|
||||
format = output['format']
|
||||
# Change the zencoder 'mpeg4' format to 'mp4' used internally
|
||||
format = 'mp4' if format == 'mpeg4' else format
|
||||
# Find a variation matching format and resolution
|
||||
variation = next((v for v in f['variations'] if v['format'] == format \
|
||||
and v['width'] == output['width']), None)
|
||||
# If found, update with delivered file size
|
||||
# TODO: calculate md5 on the storage
|
||||
if variation:
|
||||
variation['length'] = output['file_size_in_bytes']
|
||||
|
||||
put_internal('files', f, _id=ObjectId(file_id))
|
||||
return ''
|
||||
|
50
tests/test_encoding.py
Normal file
50
tests/test_encoding.py
Normal file
@ -0,0 +1,50 @@
|
||||
"""Test cases for the zencoder notifications."""
|
||||
import json
|
||||
|
||||
from common_test_class import AbstractPillarTest
|
||||
|
||||
|
||||
class ZencoderNotificationTest(AbstractPillarTest):
|
||||
|
||||
def test_missing_secret(self):
|
||||
with self.app.test_request_context():
|
||||
resp = self.client.post('/encoding/zencoder/notifications')
|
||||
self.assertEqual(401, resp.status_code)
|
||||
|
||||
def test_wrong_secret(self):
|
||||
with self.app.test_request_context():
|
||||
resp = self.client.post('/encoding/zencoder/notifications',
|
||||
headers={'X-Zencoder-Notification-Secret': 'koro'})
|
||||
self.assertEqual(401, resp.status_code)
|
||||
|
||||
def test_good_secret_missing_file(self):
|
||||
with self.app.test_request_context():
|
||||
secret = self.app.config['ZENCODER_NOTIFICATIONS_SECRET']
|
||||
resp = self.client.post('/encoding/zencoder/notifications',
|
||||
data=json.dumps({'job': {'id': 'koro-007'}}),
|
||||
headers={'X-Zencoder-Notification-Secret': secret,
|
||||
'Content-Type': 'application/json'})
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
def test_good_secret_existing_file(self):
|
||||
self.ensure_file_exists(file_overrides={
|
||||
'processing': {'backend': 'zencoder',
|
||||
'job_id': 'koro-007',
|
||||
'status': 'processing'}
|
||||
})
|
||||
|
||||
with self.app.test_request_context():
|
||||
secret = self.app.config['ZENCODER_NOTIFICATIONS_SECRET']
|
||||
resp = self.client.post('/encoding/zencoder/notifications',
|
||||
data=json.dumps({'job': {'id': 'koro-007',
|
||||
'state': 'done'},
|
||||
'outputs': [{
|
||||
'format': 'jpg',
|
||||
'width': 2048,
|
||||
'file_size_in_bytes': 15,
|
||||
}]}),
|
||||
headers={'X-Zencoder-Notification-Secret': secret,
|
||||
'Content-Type': 'application/json'})
|
||||
|
||||
# TODO: check that the file in MongoDB is actually updated properly.
|
||||
self.assertEqual(200, resp.status_code)
|
Loading…
x
Reference in New Issue
Block a user