def vidly_media_webhook(request): if not request.POST.get('xml'): return http.HttpResponseBadRequest("no 'xml'") xml_string = request.POST['xml'].strip() try: struct = xmltodict.parse(xml_string) except ExpatError: return http.HttpResponseBadRequest("Bad 'xml'") try: task = struct['Response']['Result']['Task'] try: vidly_submission = VidlySubmission.objects.get( url=task['SourceFile'], tag=task['MediaShortLink'] ) if task['Status'] == 'Finished': if not vidly_submission.finished: vidly_submission.finished = timezone.now() vidly_submission.save() event = vidly_submission.event # Awesome! # This event now has a fully working transcoded piece of # media. event.archive_time = timezone.now() event.save() # More awesome! We can start processing the transcoded media. if not event.duration: videoinfo.fetch_duration( event, save=True, verbose=settings.DEBUG ) event = Event.objects.get(id=event.id) if event.duration: if not Picture.objects.filter(event=event): videoinfo.fetch_screencapture( event, save=True, verbose=settings.DEBUG, set_first_available=True, ) elif task['Status'] == 'Error': if not vidly_submission.errored: vidly_submission.errored = timezone.now() vidly_submission.save() except VidlySubmission.DoesNotExist: # remember, we can't trust the XML since it's publically # available and exposed as a webhook pass except KeyError: # If it doesn't have a "Result" or "Task", it was just a notification # that the media was added. pass return http.HttpResponse('OK\n')
def create_chapterimages(chapter, verbose=False): def saved_file(chapter, filepath): with open(filepath, 'rb') as fp: opened = File(fp) chapter.image.save(os.path.basename(filepath), opened, save=True) return True fetch_screencapture(chapter.event, timestamps=[chapter.timestamp], import_=True, import_immediately=True, verbose=verbose, callback=partial(saved_file, chapter))
def create_all_event_pictures( event_id, set_first_available=False, video_url=None, ): event = Event.objects.get(id=event_id) videoinfo.fetch_screencapture( event, save=True, video_url=video_url, verbose=settings.DEBUG, set_first_available=set_first_available, )
def create_timestamp_pictures( event, timestamps, verbose=False, video_url=None ): fetch_screencapture( event, timestamps=timestamps, import_=True, import_immediately=True, verbose=verbose, video_url=video_url, )
def event_screencaptures(request, event): if event.status != Event.STATUS_INITIATED: return http.HttpResponseBadRequest( "Events NOT in the state of initiated." ) upload = event.upload video_url = upload.url context = {} cache_key = 'fetching-{0}'.format(event.id) # This function sets the cache `fetching-{id}` before and after calling # those functions in the videoinfo module. # The reason is that those calls might take many many seconds # and the webapp might send async calls to the event_picture view # which will inform the webapp that the slow videoinfo processes # are running and thus that the webapp shouldn't kick if off yet. seconds = event.duration if not event.duration: # it's a poor man's lock if not cache.get(cache_key): cache.set(cache_key, True, 60) seconds = videoinfo.fetch_duration( event, video_url=video_url, save=True, verbose=settings.DEBUG ) cache.delete(cache_key) event = Event.objects.get(id=event.id) context['seconds'] = seconds # The reason we can't use `if event.duration:` is because the # fetch_duration() does an inline-update instead of modifying # the instance object. no_pictures = Picture.objects.filter(event=event).count() if event.duration and not no_pictures: if not cache.get(cache_key): cache.set(cache_key, True, 60) event = Event.objects.get(id=event.id) no_pictures = videoinfo.fetch_screencapture( event, video_url=video_url, save=True, verbose=settings.DEBUG, set_first_available=not event.picture, import_immediately=True, ) cache.delete(cache_key) event = Event.objects.get(id=event.id) if no_pictures and not event.picture: # no picture has been chosen previously pictures = Picture.objects.filter(event=event).order_by('created')[:1] for picture in pictures: event.picture = picture event.save() break context['no_pictures'] = no_pictures return context
def create_chapterimages(chapter, verbose=False): def saved_file(chapter, filepath): with open(filepath, 'rb') as fp: opened = File(fp) chapter.image.save(os.path.basename(filepath), opened, save=True) return True fetch_screencapture( chapter.event, timestamps=[chapter.timestamp], import_=True, import_immediately=True, verbose=verbose, callback=partial(saved_file, chapter) )
def test_fetch_screencapture_set_first_available( self, mock_popen, rhead, p_urllib2 ): assert Picture.objects.all().count() == 0, Picture.objects.all() def mocked_urlopen(request): return StringIO(""" <?xml version="1.0"?> <Response> <Message>OK</Message> <MessageCode>7.4</MessageCode> <Success> <MediaShortLink>xxx999</MediaShortLink> <Token>MXCsxINnVtycv6j02ZVIlS4FcWP</Token> </Success> </Response> """) p_urllib2.urlopen = mocked_urlopen def mocked_head(url, **options): return Response( '', 200 ) rhead.side_effect = mocked_head ffmpeged_urls = [] sample_jpg = self.sample_jpg sample_jpg2 = self.sample_jpg2 def mocked_popen(command, **kwargs): url = command[4] ffmpeged_urls.append(url) destination = command[-1] assert os.path.isdir(os.path.dirname(destination)) class Inner: def communicate(self): out = err = '' if 'xyz123' in url: if '01.jpg' in destination: shutil.copyfile(sample_jpg, destination) else: shutil.copyfile(sample_jpg2, destination) else: raise NotImplementedError(url) return out, err return Inner() mock_popen.side_effect = mocked_popen event = Event.objects.get(title='Test event') template = Template.objects.create( name='Vid.ly Something', content="{{ tag }}" ) event.template = template event.duration = 1157 event.save() # Make sure it has a HD VidlySubmission VidlySubmission.objects.create( event=event, url='https://s3.com/asomething.mov', tag='xyz123', hd=True, ) event.template_environment = {'tag': 'xyz123'} event.save() videoinfo.fetch_screencapture(event, set_first_available=True) assert ffmpeged_urls no_screencaptures = settings.SCREENCAPTURES_NO_PICTURES pictures = Picture.objects.filter(event=event) eq_(pictures.count(), no_screencaptures) event = Event.objects.get(id=event.id) eq_(event.picture, pictures.order_by('created')[0])
def test_fetch_screencapture_set_first_available(self, mock_popen, rhead, p_urllib2): assert Picture.objects.all().count() == 0, Picture.objects.all() def mocked_urlopen(request): return StringIO(""" <?xml version="1.0"?> <Response> <Message>OK</Message> <MessageCode>7.4</MessageCode> <Success> <MediaShortLink>xxx999</MediaShortLink> <Token>MXCsxINnVtycv6j02ZVIlS4FcWP</Token> </Success> </Response> """) p_urllib2.urlopen = mocked_urlopen def mocked_head(url, **options): return Response('', 200) rhead.side_effect = mocked_head ffmpeged_urls = [] sample_jpg = self.sample_jpg sample_jpg2 = self.sample_jpg2 def mocked_popen(command, **kwargs): url = command[4] ffmpeged_urls.append(url) destination = command[-1] assert os.path.isdir(os.path.dirname(destination)) class Inner: def communicate(self): out = err = '' if 'xyz123' in url: if '01.jpg' in destination: shutil.copyfile(sample_jpg, destination) else: shutil.copyfile(sample_jpg2, destination) else: raise NotImplementedError(url) return out, err return Inner() mock_popen.side_effect = mocked_popen event = Event.objects.get(title='Test event') template = Template.objects.create(name='Vid.ly Something', content="{{ tag }}") event.template = template event.duration = 1157 event.save() # Make sure it has a HD VidlySubmission VidlySubmission.objects.create( event=event, url='https://s3.com/asomething.mov', tag='xyz123', hd=True, ) event.template_environment = {'tag': 'xyz123'} event.save() videoinfo.fetch_screencapture(event, set_first_available=True) assert ffmpeged_urls no_screencaptures = settings.SCREENCAPTURES_NO_PICTURES pictures = Picture.objects.filter(event=event) eq_(pictures.count(), no_screencaptures) event = Event.objects.get(id=event.id) eq_(event.picture, pictures.order_by('created')[0])
def test_fetch_screencapture_at_timestamp_with_callback( self, mock_popen, rhead, p_urllib2, p_log ): """This test is effectively the same as test_fetch_screencapture() but with `import_immediately=True` set. """ def mocked_urlopen(request): return StringIO(""" <?xml version="1.0"?> <Response> <Message>OK</Message> <MessageCode>7.4</MessageCode> <Success> <MediaShortLink>xxx999</MediaShortLink> <Token>MXCsxINnVtycv6j02ZVIlS4FcWP</Token> </Success> </Response> """) p_urllib2.urlopen = mocked_urlopen def mocked_head(url, **options): return Response( '', 200 ) rhead.side_effect = mocked_head ffmpeged_urls = [] sample_jpg = self.sample_jpg sample_jpg2 = self.sample_jpg2 def mocked_popen(command, **kwargs): url = command[4] ffmpeged_urls.append(url) destination = command[-1] assert os.path.isdir(os.path.dirname(destination)) class Inner: def communicate(self): out = err = '' if 'xyz123' in url: # Let's create two jpeg's in that directory if '01.jpg' in destination: shutil.copyfile(sample_jpg, destination) else: shutil.copyfile(sample_jpg2, destination) else: raise NotImplementedError(url) return out, err return Inner() mock_popen.side_effect = mocked_popen event = Event.objects.get(title='Test event') template = Template.objects.create( name='Vid.ly Something', content="{{ tag }}" ) event.duration = 1157 event.template = template event.save() # Make sure it has a HD VidlySubmission VidlySubmission.objects.create( event=event, url='https://s3.com/asomething.mov', tag='xyz123', hd=True, ) event.template_environment = {'tag': 'xyz123'} event.save() def callback(filepath): ok_(os.path.isfile(filepath)) return filepath.endswith('3.jpg') created = videoinfo.fetch_screencapture( event, timestamps=[3], callback=callback ) eq_(created, 1) eq_(len(ffmpeged_urls), 1) created = videoinfo.fetch_screencapture( event, timestamps=[2], # callback will return False for this callback=callback ) eq_(created, 0) eq_(len(ffmpeged_urls), 2)
def vidly_media_webhook(request): if not request.POST.get('xml'): return http.HttpResponseBadRequest("no 'xml'") xml_string = request.POST['xml'].strip() try: struct = xmltodict.parse(xml_string) except ExpatError: return http.HttpResponseBadRequest("Bad 'xml'") try: task = struct['Response']['Result']['Task'] try: vidly_submission = VidlySubmission.objects.get( url=task['SourceFile'], tag=task['MediaShortLink']) if task['Status'] == 'Finished': if not vidly_submission.finished: vidly_submission.finished = timezone.now() vidly_submission.save() event = vidly_submission.event if (task['Private'] == 'false' and event.privacy != Event.PRIVACY_PUBLIC): # the event is private but the video is not vidly.update_media_protection( vidly_submission.tag, True # make it private ) if not vidly_submission.token_protection: vidly_submission.token_protection = True vidly_submission.save() # Awesome! # This event now has a fully working transcoded piece of # media. if (event.status == Event.STATUS_PENDING or event.status == Event.STATUS_PROCESSING): event.status = Event.STATUS_SCHEDULED event.archive_time = timezone.now() event.save() # More awesome! We can start processing the transcoded media. # XXX move this to a background task. if not event.duration: videoinfo.fetch_duration(event, save=True, verbose=settings.DEBUG) event = Event.objects.get(id=event.id) if event.duration: if not Picture.objects.filter(event=event): videoinfo.fetch_screencapture( event, save=True, verbose=settings.DEBUG, set_first_available=True, ) elif task['Status'] == 'Error': if not vidly_submission.errored: vidly_submission.errored = timezone.now() vidly_submission.save() except VidlySubmission.DoesNotExist: # remember, we can't trust the XML since it's publicly # available and exposed as a webhook pass except KeyError: # If it doesn't have a "Result" or "Task", it was just a notification # that the media was added. pass return http.HttpResponse('OK\n')