Ejemplo n.º 1
0
def test_transcode_job(mocker, status, expected_status):
    """
    Test that video status is updated properly after a transcode job is successfully created
    """
    video = VideoFactory.create(status=status)
    videofile = VideoFileFactory.create(video=video)

    prefix = RETRANSCODE_FOLDER if status == VideoStatus.RETRANSCODE_SCHEDULED else ""
    preset = {
        "Key":
        f"{prefix}transcoded/" + video.hexkey + "/video_1351620000001-000040",
        "PresetId": "1351620000001-000040",
        "SegmentDuration": "10.0",
    }
    if status != VideoStatus.RETRANSCODE_SCHEDULED:
        preset["ThumbnailPattern"] = ("thumbnails/" + video.hexkey +
                                      "/video_thumbnail_{count}")

    mocker.patch.multiple(
        "cloudsync.tasks.settings",
        ET_PRESET_IDS=("1351620000001-000040", "1351620000001-000020"),
        AWS_REGION="us-east-1",
        ET_PIPELINE_ID="foo",
        ENVIRONMENT="test",
    )
    mock_encoder = mocker.patch("cloudsync.api.VideoTranscoder.encode")
    mock_delete_objects = mocker.patch("cloudsync.api.delete_s3_objects")
    mocker.patch("ui.models.tasks")

    api.transcode_video(video, videofile)  # pylint: disable=no-value-for-parameter
    mock_encoder.assert_called_once_with(
        {"Key": videofile.s3_object_key},
        [
            preset,
            {
                "Key": f"{prefix}transcoded/" + video.hexkey +
                "/video_1351620000001-000020",
                "PresetId": "1351620000001-000020",
                "SegmentDuration": "10.0",
            },
        ],
        Playlists=[{
            "Format":
            "HLSv3",
            "Name":
            f"{prefix}transcoded/" + video.hexkey + "/video__index",
            "OutputKeys": [
                f"{prefix}transcoded/" + video.hexkey +
                "/video_1351620000001-000040",
                f"{prefix}transcoded/" + video.hexkey +
                "/video_1351620000001-000020",
            ],
        }],
        UserMetadata={"pipeline": "odl-video-service-test"},
    )
    assert len(video.encode_jobs.all()) == 1
    assert mock_delete_objects.call_count == (
        1 if status == VideoStatus.RETRANSCODE_SCHEDULED else 0)
    assert Video.objects.get(id=video.id).status == expected_status
Ejemplo n.º 2
0
def test_post_hls_to_edx_wrong_type(mocker):
    """
    post_hls_to_edx should raise an exception if the given video file is not
    configured correctly for posting to edX
    """
    video_file = VideoFileFactory.create(encoding=EncodingNames.ORIGINAL)
    with pytest.raises(Exception):
        api.post_hls_to_edx(video_file)
Ejemplo n.º 3
0
def test_post_hls_to_edx_no_endpoints(mocker):
    """post_hls_to_edx should log an error if no endpoints are configured for some video's collection"""
    patched_log_error = mocker.patch("ui.api.log.error")
    video_file = VideoFileFactory.create(
        encoding=EncodingNames.HLS,
        video__collection__edx_course_id="some-course-id",
    )
    responses = api.post_hls_to_edx(video_file)
    patched_log_error.assert_called_once()
    assert responses == {}
Ejemplo n.º 4
0
def edx_api_scenario():
    """Fixture that provides a VideoFile with the correct properties to post to edX"""
    course_id = "course-v1:abc"
    video_file = VideoFileFactory.create(
        encoding=EncodingNames.HLS,
        video__title="My Video",
        video__collection__edx_course_id=course_id,
    )
    collection_edx_endpoint = CollectionEdxEndpointFactory(
        collection=video_file.video.collection
    )
    return SimpleNamespace(
        video_file=video_file,
        course_id=course_id,
        collection_endpoint=collection_edx_endpoint.edx_endpoint,
    )
Ejemplo n.º 5
0
def test_video_file_can_add_to_edx(encoding, edx_course_id, expected):
    """Test that VideoFile.can_add_to_edx returns True under the right conditions"""
    video_files = VideoFileFactory.create(
        encoding=encoding, video__collection__edx_course_id=edx_course_id)
    assert video_files.can_add_to_edx is expected
Ejemplo n.º 6
0
def test_transcode_job_failure(mocker, status, error_status):
    """
    Test that video status is updated properly after a transcode or retranscode job creation fails
    """
    mocker.patch("cloudsync.api.delete_s3_objects")
    video = VideoFactory.create(status=status)
    videofile = VideoFileFactory.create(video=video)

    job_result = {
        "Job": {
            "Id": "1498220566931-qtmtcu",
            "Status": "Error"
        },
        "Error": {
            "Code": 200,
            "Message": "FAIL"
        },
    }
    mocker.patch.multiple(
        "cloudsync.tasks.settings",
        ET_PRESET_IDS=("1351620000001-000020", ),
        AWS_REGION="us-east-1",
        ET_PIPELINE_ID="foo",
        ENVIRONMENT="test",
    )
    mocker.patch("ui.models.tasks")
    mock_encoder = mocker.patch(
        "cloudsync.api.VideoTranscoder.encode",
        side_effect=ClientError(error_response=job_result,
                                operation_name="ReadJob"),
    )
    with pytest.raises(ClientError):
        api.transcode_video(video, videofile)

    prefix = "" if status == VideoStatus.TRANSCODING else RETRANSCODE_FOLDER
    preset = {
        "Key":
        f"{prefix}transcoded/" + video.hexkey + "/video_1351620000001-000020",
        "PresetId": "1351620000001-000020",
        "SegmentDuration": "10.0",
    }
    if status == VideoStatus.TRANSCODING:
        preset["ThumbnailPattern"] = ("thumbnails/" + video.hexkey +
                                      "/video_thumbnail_{count}")
    mock_encoder.assert_called_once_with(
        {"Key": videofile.s3_object_key},
        [preset],
        Playlists=[{
            "Format":
            "HLSv3",
            "Name":
            f"{prefix}transcoded/" + video.hexkey + "/video__index",
            "OutputKeys": [
                f"{prefix}transcoded/" + video.hexkey +
                "/video_1351620000001-000020"
            ],
        }],
        UserMetadata={"pipeline": "odl-video-service-test"},
    )
    assert len(video.encode_jobs.all()) == 1
    assert Video.objects.get(id=video.id).status == error_status
Ejemplo n.º 7
0
def test_process_transcode_results(mocker, status):
    """
    Verify that a videofile object is created for each output in the job JSON, and a thumbnail
    is created for each S3 object in the appropriate bucket virtual subfolder.
    """
    mock_move_s3_objects = mocker.patch("cloudsync.api.move_s3_objects")
    video = VideoFactory.create(status=status)
    VideoFileFactory.create(video=video)
    # We need to create the thumbnail bucket since this is all in the Moto virtual AWS account
    conn = boto3.resource("s3", region_name="us-east-1")
    bucket = conn.create_bucket(Bucket=settings.VIDEO_S3_THUMBNAIL_BUCKET)
    p = RETRANSCODE_FOLDER if status == VideoStatus.RETRANSCODING else ""

    # Throw a fake thumbnail in the bucket:
    data = io.BytesIO(b"00000001111111")
    bucket.upload_fileobj(
        data,
        "thumbnails/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_00001.jpg")

    job = {
        "Id":
        "1498765896748-e0p0qr",
        "Input": {
            "Key": "1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi.mp4"
        },
        "Inputs": [{
            "Key": "1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi.mp4"
        }],
        "Output": {
            "Id": "1",
            "Key":
            f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700489769-iyi2t4",
            "PresetId": "1498700489769-iyi2t4",
            "SegmentDuration": "10.0",
            "Status": "Complete",
        },
        "Outputs": [
            {
                "Id": "1",
                "Key":
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700489769-iyi2t4",
                "PresetId": "1498700489769-iyi2t4",
                "SegmentDuration": "10.0",
                "Status": "Complete",
                "ThumbnailPattern":
                "thumbnails/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_{count}",
                "Watermarks": [],
                "Width": 1280,
            },
            {
                "Id": "2",
                "Key":
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700403561-zc5oo5",
                "PresetId": "1498700403561-zc5oo5",
                "SegmentDuration": "10.0",
                "Status": "Complete",
                "Watermarks": [],
                "Width": 1280,
            },
            {
                "Id": "3",
                "Key":
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700578799-qvvjor",
                "PresetId": "1498700578799-qvvjor",
                "SegmentDuration": "10.0",
                "Status": "Complete",
                "Watermarks": [],
                "Width": 854,
            },
            {
                "Id": "4",
                "Key":
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700649488-6t9m3h",
                "PresetId": "1498700649488-6t9m3h",
                "SegmentDuration": "10.0",
                "Status": "Complete",
                "Watermarks": [],
                "Width": 640,
            },
        ],
        "PipelineId":
        "1497455687488-evsuze",
        "Playlists": [{
            "Format":
            "HLSv4",
            "Name":
            f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi__index",
            "OutputKeys": [
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700489769-iyi2t4",
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700403561-zc5oo5",
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700578799-qvvjor",
                f"{p}transcoded/1/05a06f21-7625-4c20-b416-ae161f31722a/lastjedi_1498700649488-6t9m3h",
            ],
            "Status":
            "Complete",
        }],
        "Status":
        "Complete",
    }

    MockClientET.preset = {
        "Preset": {
            "Thumbnails": {
                "MaxHeight": 190,
                "MaxWidth": 100
            }
        }
    }
    mocker.patch("ui.utils.get_transcoder_client", return_value=MockClientET())
    api.process_transcode_results(video, job)
    assert len(video.videofile_set.all()) == 2
    assert len(video.videothumbnail_set.all()) == 1
    assert mock_move_s3_objects.call_count == (
        1 if status == VideoStatus.RETRANSCODING else 0)
Ejemplo n.º 8
0
def test_post_hls_to_edx(mocker):
    """post_hls_to_edx task should load a VideoFile and call an internal API function to post to edX"""
    patched_api_method = mocker.patch("ui.tasks.ovs_api.post_hls_to_edx")
    video_file = VideoFileFactory.create()
    tasks.post_hls_to_edx.delay(video_file.id)
    patched_api_method.assert_called_once_with(video_file)