diff --git a/dj_elastictranscoder/admin.py b/dj_elastictranscoder/admin.py index c7ad991..af090b2 100644 --- a/dj_elastictranscoder/admin.py +++ b/dj_elastictranscoder/admin.py @@ -1,7 +1,20 @@ from django.contrib import admin -from .models import EncodeJob +from .models import EncodeJob, Upload, Job + + +class JobAdmin(admin.ModelAdmin): + pass + + +admin.site.register(Job, JobAdmin) + + +admin.site.register(Upload) + class EncodeJobAdmin(admin.ModelAdmin): list_display = ('id', 'state', 'message') list_filters = ('state',) + + admin.site.register(EncodeJob, EncodeJobAdmin) diff --git a/dj_elastictranscoder/management/__init__.py b/dj_elastictranscoder/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dj_elastictranscoder/management/commands/__init__.py b/dj_elastictranscoder/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dj_elastictranscoder/management/commands/et_create_job.py b/dj_elastictranscoder/management/commands/et_create_job.py new file mode 100644 index 0000000..8d3d057 --- /dev/null +++ b/dj_elastictranscoder/management/commands/et_create_job.py @@ -0,0 +1,13 @@ +from django.core.management.base import BaseCommand + +from dj_elastictranscoder.models import Job +from dj_elastictranscoder.utils import et_create_job + + +class Command(BaseCommand): + help = "Sends Jobs to be executed remotely." + + def handle(self, *args, **options): + for pk in args: + job = Job.objects.get(pk=pk) + et_create_job(job) diff --git a/dj_elastictranscoder/management/commands/et_cron.py b/dj_elastictranscoder/management/commands/et_cron.py new file mode 100644 index 0000000..2812086 --- /dev/null +++ b/dj_elastictranscoder/management/commands/et_cron.py @@ -0,0 +1,15 @@ +from django.core.management.base import BaseCommand + +from dj_elastictranscoder.models import Job +from dj_elastictranscoder.utils import et_create_job, et_read_job + + +class Command(BaseCommand): + help = "Sends and receive jobs" + + def handle(self, *args, **options): + for job in Job.objects.filter(et_job_id=""): + et_create_job(job) + + for job in Job.objects.filter(output=""): + et_read_job(job) diff --git a/dj_elastictranscoder/management/commands/et_read_job.py b/dj_elastictranscoder/management/commands/et_read_job.py new file mode 100644 index 0000000..5c107a6 --- /dev/null +++ b/dj_elastictranscoder/management/commands/et_read_job.py @@ -0,0 +1,13 @@ +from django.core.management.base import BaseCommand + +from dj_elastictranscoder.models import Job +from dj_elastictranscoder.utils import et_read_job + + +class Command(BaseCommand): + help = "Reads Jobs and update it's info on Database." + + def handle(self, *args, **options): + for pk in args: + job = Job.objects.get(pk=pk) + et_read_job(job) diff --git a/dj_elastictranscoder/migrations/0002_auto__add_job__add_upload.py b/dj_elastictranscoder/migrations/0002_auto__add_job__add_upload.py new file mode 100644 index 0000000..d5de9ef --- /dev/null +++ b/dj_elastictranscoder/migrations/0002_auto__add_job__add_upload.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Job' + db.create_table(u'dj_elastictranscoder_job', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('pipeline_id', self.gf('django.db.models.fields.CharField')(default='1402976603358-rwcmfz', max_length=32)), + ('upload', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dj_elastictranscoder.Upload'])), + ('preset', self.gf('django.db.models.fields.CharField')(default='1351620000001-100070', max_length=20)), + ('et_job_id', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('output', self.gf('django.db.models.fields.files.FileField')(max_length=100, blank=True)), + )) + db.send_create_signal(u'dj_elastictranscoder', ['Job']) + + # Adding unique constraint on 'Job', fields ['upload', 'preset'] + db.create_unique(u'dj_elastictranscoder_job', ['upload_id', 'preset']) + + # Adding model 'Upload' + db.create_table(u'dj_elastictranscoder_upload', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('video', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + )) + db.send_create_signal(u'dj_elastictranscoder', ['Upload']) + + + def backwards(self, orm): + # Removing unique constraint on 'Job', fields ['upload', 'preset'] + db.delete_unique(u'dj_elastictranscoder_job', ['upload_id', 'preset']) + + # Deleting model 'Job' + db.delete_table(u'dj_elastictranscoder_job') + + # Deleting model 'Upload' + db.delete_table(u'dj_elastictranscoder_upload') + + + models = { + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'dj_elastictranscoder.encodejob': { + 'Meta': {'object_name': 'EncodeJob'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'state': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'db_index': 'True'}) + }, + u'dj_elastictranscoder.job': { + 'Meta': {'unique_together': "(('upload', 'preset'),)", 'object_name': 'Job'}, + 'et_job_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'output': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'pipeline_id': ('django.db.models.fields.CharField', [], {'default': "'1402976603358-rwcmfz'", 'max_length': '32'}), + 'preset': ('django.db.models.fields.CharField', [], {'default': "'1351620000001-100070'", 'max_length': '20'}), + 'upload': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['dj_elastictranscoder.Upload']"}) + }, + u'dj_elastictranscoder.upload': { + 'Meta': {'object_name': 'Upload'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'video': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) + } + } + + complete_apps = ['dj_elastictranscoder'] \ No newline at end of file diff --git a/dj_elastictranscoder/models.py b/dj_elastictranscoder/models.py index ee35afc..ace7ab6 100644 --- a/dj_elastictranscoder/models.py +++ b/dj_elastictranscoder/models.py @@ -1,7 +1,63 @@ +# coding: utf-8 + from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.generic import GenericForeignKey +from storages.backends.s3boto import S3BotoStorage + + +storage = S3BotoStorage() + + +class Upload(models.Model): + video = models.FileField(storage=storage, upload_to="videos/upload") + + def __unicode__(self): + return self.video.name + + +class Job(models.Model): + PIPELINE_ID = ( + ('1402976603358-rwcmfz', 'Mediacenter'), + ) + + PRESET = ( + ('1351620000001-100070', 'Web: Facebook, SmugMug, Vimeo, YouTube'), + ) + + pipeline_id = models.CharField( + choices=PIPELINE_ID, + default=PIPELINE_ID[0][0], + max_length=32) + + upload = models.ForeignKey("Upload") + + preset = models.CharField( + choices=PRESET, + default=PRESET[0][0], + max_length=20, + ) + + et_job_id = models.CharField( + blank=True, + max_length=100, + ) + + output = models.FileField( + blank=True, + storage=storage, + upload_to="videos/output", + ) + + class Meta: + unique_together = ("upload", "preset", ) + + def __unicode__(self): + if self.output: + return u"%s as %s DONE" % (self.upload, self.get_preset_display()) + return u"%s as %s" % (self.upload, self.get_preset_display()) + class EncodeJob(models.Model): STATE_CHOICES = ( @@ -11,10 +67,17 @@ class EncodeJob(models.Model): (3, 'Warning'), (4, 'Complete'), ) + id = models.CharField(max_length=100, primary_key=True) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() - state = models.PositiveIntegerField(choices=STATE_CHOICES, default=0, db_index=True) + + state = models.PositiveIntegerField( + choices=STATE_CHOICES, + db_index=True, + default=0, + ) + content_object = GenericForeignKey() message = models.TextField() created_at = models.DateTimeField(auto_now_add=True) diff --git a/dj_elastictranscoder/transcoder.py b/dj_elastictranscoder/transcoder.py index 0d5062d..c0e392b 100644 --- a/dj_elastictranscoder/transcoder.py +++ b/dj_elastictranscoder/transcoder.py @@ -7,7 +7,8 @@ class Transcoder(object): - def __init__(self, pipeline_id, region=None, access_key_id=None, secret_access_key=None): + def __init__(self, pipeline_id, region=None, access_key_id=None, + secret_access_key=None): self.pipeline_id = pipeline_id if not region: @@ -19,10 +20,10 @@ def __init__(self, pipeline_id, region=None, access_key_id=None, secret_access_k self.aws_access_key_id = access_key_id if not secret_access_key: - secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) + secret_access_key = \ + getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) self.aws_secret_access_key = secret_access_key - if self.aws_access_key_id is None: assert False, 'Please provide AWS_ACCESS_KEY_ID' @@ -32,21 +33,22 @@ def __init__(self, pipeline_id, region=None, access_key_id=None, secret_access_k if self.aws_region is None: assert False, 'Please provide AWS_REGION' - def encode(self, input_name, outputs): - encoder = elastictranscoder.connect_to_region( - self.aws_region, + et = self.get_et() + self.message = \ + et.create_job(self.pipeline_id, input_name, outputs=outputs) + + def get_et(self): + return elastictranscoder.connect_to_region( + self.aws_region, aws_access_key_id=self.aws_access_key_id, aws_secret_access_key=self.aws_secret_access_key) - self.message = encoder.create_job(self.pipeline_id, input_name, outputs=outputs) - - def create_job_for_object(self, obj): content_type = ContentType.objects.get_for_model(obj) - job = EncodeJob() job.id = self.message['Job']['Id'] job.content_type = content_type job.object_id = obj.id job.save() + return self.message['Job']['Id'] diff --git a/dj_elastictranscoder/utils.py b/dj_elastictranscoder/utils.py new file mode 100644 index 0000000..02bfd31 --- /dev/null +++ b/dj_elastictranscoder/utils.py @@ -0,0 +1,26 @@ +from django.utils.text import slugify + +from .transcoder import Transcoder + + +def et_create_job(job): + if not job.output: + outputs = [{ + 'Key': 'outputs/%s.mp4' % slugify(unicode(job)), + 'PresetId': job.preset + }] + + transcoder = Transcoder(job.pipeline_id) + transcoder.encode({'Key': job.upload.video.name}, outputs) + job.et_job_id = transcoder.create_job_for_object(job) + job.save(update_fields=['et_job_id']) + + +def et_read_job(job): + transcoder = Transcoder(job.pipeline_id) + et = transcoder.get_et() + data = et.read_job(job.et_job_id) + for output in data['Job']['Outputs']: + if output['Status'] == 'Complete': + job.output = output['Key'] + job.save(update_fields=['output'])