Show:

File: app/components/exp-lookit-stop-recording/component.js

import Ember from 'ember';
import layout from './template';
import ExpFrameBaseComponent from '../exp-frame-base/component';
import ExpandAssets from '../../mixins/expand-assets';
import isColor, {colorSpecToRgbaArray} from '../../utils/is-color';
import { imageAssetOptions, videoAssetOptions } from '../../mixins/expand-assets';

let {
    $
} = Ember;

/**
 * @module exp-player
 * @submodule frames
 */

/**
* Dedicated frame to stop session recording.
*
* This frame will take a few seconds to upload an ongoing session-level recording, then proceed
* immediately to the next frame. (See https://lookit.readthedocs.io/en/develop/researchers-create-experiment.html?highlight=startSessionRecording#recording-webcam-video
* for information about session-level vs. individual-frame recording.)
*
* It will time out after a default of 5 minutes of waiting for the upload to complete, or
* after 5 seconds of not seeing any progress (i.e. something went wrong with starting the
* upload process). If there is no active session recording, it proceeds immediately.
*
*
*
* (You could also set stopSessionRecording to true on any frame, but you generally wouldn't
* get any specialized functionality for displaying a nice message about upload progress.)
*
* Just like for exp-lookit-calibration, you can display a video or an optionally animated
* image (see below for examples of each) as a placeholder while getting recording started.
*
* For details about specifying media locations, please see
* https://lookit.readthedocs.io/en/develop/researchers-prep-stimuli.html?highlight=basedir#directory-structure
*
* Example usage:

```json

    "stop-recording-with-image": {
        "kind": "exp-lookit-stop-recording",
        "baseDir": "https://www.mit.edu/~kimscott/placeholderstimuli/",
        "videoTypes": [
            "webm",
            "mp4"
        ],
        "image": "peekaboo_remy.jpg",
        "imageAnimation": "spin"
        "displayFullscreen": true
    },

    "stop-recording-with-video": {
        "kind": "exp-lookit-stop-recording",
        "baseDir": "https://www.mit.edu/~kimscott/placeholderstimuli/",
        "videoTypes": [
            "webm",
            "mp4"
        ],
        "video": "attentiongrabber",
        "displayFullscreen": true
    },

* ```
* @class Exp-lookit-stop-recording
* @extends Exp-frame-base
* @uses Expand-assets
*/

export default ExpFrameBaseComponent.extend(ExpandAssets, {
    layout: layout,
    type: 'exp-lookit-stop-recording',

    fullScreenElementId: 'experiment-player',
    fsButtonID: 'fsButton',

    endSessionRecording: true,
    sessionMaxUploadSeconds: 3000, // 5 minutes - generous default for possibly long recording

    hasStartedUpload: false,

    progressTimer: null,
    allowProceedTimer: null,

    assetsToExpand: {
        'audio': [],
        'video': [
            'video'
        ],
        'image': [
            'image'
        ]
    },

    frameSchemaProperties: {
        /**
         * Whether to display this frame in full-screen mode         *
         * @property {Boolean} displayFullscreen
         * @default true
         */
        displayFullscreen: {
            type: 'boolean',
            description: 'Whether to display this frame in full-screen mode',
            default: true
        },
        /**
         * Color of background. See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
         * for acceptable syntax: can use color names ('blue', 'red', 'green', etc.), or
         * rgb hex values (e.g. '#800080' - include the '#')
         *
         * @property {String} backgroundColor
         * @default 'black'
         */
        backgroundColor: {
            type: 'string',
            description: 'Color of background',
            default: 'white'
        },
        /**
         * Video to play (looping) while waiting. Supply
         * either a video or image, not both.
         *
         * This can be either an array of {src: 'url', type: 'MIMEtype'} objects or
         * just a string like `attentiongrabber` to rely on the `baseDir` and `videoTypes`
         * to generate full paths.
         *
         * @property {Object[]} video
         * @default []
         */
        video: {
            anyOf: videoAssetOptions,
            description: 'list of objects specifying video src and type for calibration audio',
            default: []
        },
        /**
         * Image to display while waiting. Supply
         * either a video or image, not both.
         *
         * This can be either a full URL or just the filename (e.g. "star.png") to
         * use the full path based on `baseDir` (e.g. `baseDir/img/star.png`).
         *
         * @property {String} image
         * @default []
         */
        image: {
            anyOf: imageAssetOptions,
            description: 'Image to display while uploading',
            default: ''
        },

        /**
         * Which animation to use for the image. Options are 'bounce', 'spin',
         * or '' (empty to not animate).
         *
         * @property {String} imageAnimation
         * @default []
         */
        imageAnimation: {
            type: 'string',
            enum: ['bounce', 'spin', ''],
            description: 'Which animation to use for the image.',
            default: 'spin'
        }
    },

    // TODO: store some data about what happened!
    meta: {
        data: {
            type: 'object',
            properties: {
            }
        }
    },


    didInsertElement() {
        this._super(...arguments);
        this.set('hasVideo', this.get('video').length > 0);

        // Apply background colors
        let colorSpec = this.get('backgroundColor');
        if (isColor(colorSpec)) {
            $('div.exp-lookit-start-stop-recording').css('background-color', colorSpec);
            // Set text color so it'll be visible (black or white depending on how dark background is). Use style
            // so this applies whenever pause text actually appears.
            let colorSpecRGBA = colorSpecToRgbaArray(colorSpec);
            let textColor = (colorSpecRGBA[0] + colorSpecRGBA[1] + colorSpecRGBA[2] > 128 * 3) ? 'black' : 'white';
            //$(`<style>.exp-lookit-start-stop-recording p.wait-for-video { color: ${textColor}; }</style>`).appendTo('div.exp-lookit-start-stop-recording');
            $('p.wait-for-video').css('color', textColor);
        } else {
            console.warn('Invalid background color provided; not applying.');
        }

        // Apply image animation class
        if (this.get('image')) {
            $('#placeholder-image').addClass(this.get('imageAnimation'));
        }

        // Make sure we're already recording at the session level!
        if (!this.get('sessionRecordingInProgress')) {
            /**
             * If there's no active session recording so this frame is proceeding
             * immediately.
             *
             * @event warningNoActiveSessionRecording
             */
            this.send('setTimeEvent', 'warningNoActiveSessionRecording');
            this.send('next');
        }

        var _this = this;
        this.stopSessionRecorder().finally(() => {
            _this.send('next');
        });

        this.set('progressTimer', window.setInterval(function() {
            let msg = $('.pipeMsgOverlay').html();
            let match = msg.match(/\d*%/);
            if (match) {
                $('#progress').html(`Uploading video... ${match[0]}`);
                $('.progress-bar').css('width', match);
                _this.set('hasStartedUpload', true);
            } else if (msg) {
                $('#progress').html(msg);
            } else {
                $('#progress').html('Uploading video...')
            }
        }, 100));

        this.set('allowProceedTimer', window.setTimeout(function() {
            if (!_this.get('hasStartedUpload')) {
                /**
                 * If no progress update about upload is available within 10s, and
                 * frame proceeds automatically. Otherwise if the upload has started
                 * (e.g. we know it is 10% done) it will continue waiting.
                 *
                 * @event warningUploadTimeoutError
                 */
                _this.send('setTimeEvent', 'warningUploadTimeoutError');
                _this.send('next');
            }
        }, 5000));

    },

    willDestroyElement() {
        window.clearInterval(this.get('progressTimer'));
        window.clearInterval(this.get('allowProceedTimer'));
        this._super(...arguments);
    }

});