Passed
Push — master ( 2baee7...d0526d )
by Jacob
01:42
created

yui/src/recording/js/videomodule.js   A

Complexity

Total Complexity 35
Complexity/F 2.33

Size

Lines of Code 219
Function Count 15

Duplication

Duplicated Lines 219
Ratio 100 %

Importance

Changes 29
Bugs 1 Features 0
Metric Value
cc 0
nc 12
dl 219
loc 219
rs 9
c 29
b 1
f 0
wmc 35
mnd 5
bc 27
fnc 15
bpm 1.8
cpm 2.3333
noi 0

3 Functions

Rating   Name   Duplication   Size   Complexity  
B M.atto_recordrtc.videomodule.capture_audio_video 26 26 1
B M.atto_recordrtc.videomodule.init 130 130 1
A M.atto_recordrtc.videomodule.stop_recording 53 53 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
//
16
17
/**
18
 * Atto recordrtc library functions
19
 *
20
 * @package    atto_recordrtc
21
 * @author     Jesus Federico (jesus [at] blindsidenetworks [dt] com)
22
 * @author     Jacob Prud'homme (jacob [dt] prudhomme [at] blindsidenetworks [dt] com)
23
 * @copyright  2017 Blindside Networks Inc.
24
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25
 */
26
27
// JSHint directives.
28
/*jshint es5: true */
29
/*jshint onevar: false */
30
/*jshint shadow: true */
31
/*global M */
32
33
// Scrutinizer CI directives.
34
/** global: M */
35
/** global: Y */
36
37 View Code Duplication
M.atto_recordrtc = M.atto_recordrtc || {};
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
38
39
// Shorten access to M.atto_recordrtc.commonmodule namespace.
40
var cm = M.atto_recordrtc.commonmodule;
41
42
M.atto_recordrtc.videomodule = {
43
    init: function(scope) {
44
        // Assignment of global variables.
45
        cm.editorScope = scope; // Allows access to the editor's "this" context.
46
        cm.alertWarning = Y.one('div#alert-warning');
47
        cm.alertDanger = Y.one('div#alert-danger');
48
        cm.player = Y.one('video#player');
49
        cm.playerDOM = document.querySelector('video#player');
50
        cm.startStopBtn = Y.one('button#start-stop');
51
        cm.uploadBtn = Y.one('button#upload');
52
        cm.recType = 'video';
53
        cm.olderMoodle = scope.get('oldermoodle');
54
        // Extract the numbers from the string, and convert to bytes.
55
        cm.maxUploadSize = window.parseInt(scope.get('maxrecsize').match(/\d+/)[0], 10) * Math.pow(1024, 2);
56
57
        // Show alert and redirect user if connection is not secure.
58
        cm.check_secure();
59
        // Show alert if using non-ideal browser.
60
        cm.check_browser();
61
62
        // Run when user clicks on "record" button.
63
        cm.startStopBtn.on('click', function() {
64
            cm.startStopBtn.set('disabled', true);
65
66
            // If button is displaying "Start Recording" or "Record Again".
67
            if ((cm.startStopBtn.get('textContent') === M.util.get_string('startrecording', 'atto_recordrtc')) ||
68
                (cm.startStopBtn.get('textContent') === M.util.get_string('recordagain', 'atto_recordrtc')) ||
69
                (cm.startStopBtn.get('textContent') === M.util.get_string('recordingfailed', 'atto_recordrtc'))) {
70
                // Make sure the upload button is not shown.
71
                cm.uploadBtn.ancestor().ancestor().addClass('hide');
72
73
                // Change look of recording button.
74
                if (!cm.olderMoodle) {
75
                    cm.startStopBtn.replaceClass('btn-outline-danger', 'btn-danger');
76
                }
77
78
                // Empty the array containing the previously recorded chunks.
79
                cm.chunks = [];
80
                cm.blobSize = 0;
81
82
                // Initialize common configurations.
83
                var commonConfig = {
84
                    // When the stream is captured from the microphone/webcam.
85
                    onMediaCaptured: function(stream) {
86
                        // Make video stream available at a higher level by making it a property of the common module.
87
                        cm.stream = stream;
88
89
                        cm.start_recording(cm.recType, cm.stream);
90
                    },
91
92
                    // Revert button to "Record Again" when recording is stopped.
93
                    onMediaStopped: function(btnLabel) {
94
                        cm.startStopBtn.set('textContent', btnLabel);
95
                        cm.startStopBtn.set('disabled', false);
96
                        if (!cm.olderMoodle) {
97
                            cm.startStopBtn.replaceClass('btn-danger', 'btn-outline-danger');
98
                        }
99
                    },
100
101
                    // Handle recording errors.
102
                    onMediaCapturingFailed: function(error) {
103
                        var btnLabel = M.util.get_string('recordingfailed', 'atto_recordrtc');
104
                        var treatAsStopped = function() {
105
                            commonConfig.onMediaStopped(btnLabel);
106
                        };
107
108
                        // Handle getUserMedia-thrown errors.
109
                        // After alert, proceed to treat as stopped recording, or close dialogue.
110
                        switch (error.name) {
111
                            case 'AbortError':
112
                                cm.show_alert('gumabort', treatAsStopped);
113
114
                                break;
115
                            case 'NotAllowedError':
116
                                cm.show_alert('gumnotallowed', treatAsStopped);
117
118
                                break;
119
                            case 'NotFoundError':
120
                                cm.show_alert('gumnotfound', treatAsStopped);
121
122
                                break;
123
                            case 'NotReadableError':
124
                                cm.show_alert('gumnotreadable', treatAsStopped);
125
126
                                break;
127
                            case 'OverConstrainedError':
128
                                cm.show_alert('gumoverconstrained', treatAsStopped);
129
130
                                break;
131
                            case 'SecurityError':
132
                                cm.show_alert('gumsecurity', function() {
133
                                    cm.editorScope.closeDialogue(cm.editorScope);
134
                                });
135
136
                                break;
137
                            case 'TypeError':
138
                                cm.show_alert('gumtype', treatAsStopped);
139
140
                                break;
141
                            default:
142
                                break;
143
                        }
144
                    }
145
                };
146
147
                // Show video tag without controls to view webcam stream.
148
                cm.player.ancestor().ancestor().removeClass('hide');
149
                cm.player.set('controls', false);
150
151
                // Capture audio+video stream from webcam/microphone.
152
                M.atto_recordrtc.videomodule.capture_audio_video(commonConfig);
153
            } else { // If button is displaying "Stop Recording".
154
                // First of all clears the countdownTicker.
155
                window.clearInterval(cm.countdownTicker);
156
157
                // Disable "Record Again" button for 1s to allow background processing (closing streams).
158
                window.setTimeout(function() {
159
                    cm.startStopBtn.set('disabled', false);
160
                }, 1000);
161
162
                // Stop recording.
163
                M.atto_recordrtc.videomodule.stop_recording(cm.stream);
164
165
                // Change button to offer to record again.
166
                cm.startStopBtn.set('textContent', M.util.get_string('recordagain', 'atto_recordrtc'));
167
                if (!cm.olderMoodle) {
168
                    cm.startStopBtn.replaceClass('btn-danger', 'btn-outline-danger');
169
                }
170
            }
171
        });
172
    },
173
174
    // Setup to get audio+video stream from microphone/webcam.
175
    capture_audio_video: function(config) {
176
        cm.capture_user_media(
177
            // Media constraints.
178
            {
179
                audio: true,
180
                video: {
181
                    width: {ideal: 640},
182
                    height: {ideal: 480}
183
                }
184
            },
185
186
            // Success callback.
187
            function(audioVideoStream) {
188
                // Set video player source to microphone+webcam stream, and play it back as it's recording.
189
                cm.playerDOM.srcObject = audioVideoStream;
190
                cm.playerDOM.play();
191
192
                config.onMediaCaptured(audioVideoStream);
193
            },
194
195
            // Error callback.
196
            function(error) {
197
                config.onMediaCapturingFailed(error);
198
            }
199
        );
200
    },
201
202
    stop_recording: function(stream) {
203
        // Stop recording microphone stream.
204
        cm.mediaRecorder.stop();
205
206
        // Stop each individual MediaTrack.
207
        stream.getTracks().forEach(function(track) {
208
            track.stop();
209
        });
210
211
        // Set source of video player.
212
        var blob = new window.Blob(cm.chunks, {type: cm.mediaRecorder.mimeType});
213
        cm.player.set('src', window.URL.createObjectURL(blob));
214
215
        // Enable controls for video player, and unmute.
216
        cm.player.set('muted', false);
217
        cm.player.set('controls', true);
218
219
        // Show upload button.
220
        cm.uploadBtn.ancestor().ancestor().removeClass('hide');
221
        cm.uploadBtn.set('textContent', M.util.get_string('attachrecording', 'atto_recordrtc'));
222
        cm.uploadBtn.set('disabled', false);
223
224
        // Handle when upload button is clicked.
225
        cm.uploadBtn.on('click', function() {
226
            // Trigger error if no recording has been made.
227
            if (!cm.player.get('src') || cm.chunks === []) {
228
                cm.show_alert('norecordingfound');
229
            } else {
230
                cm.uploadBtn.set('disabled', true);
231
232
                // Upload recording to server.
233
                cm.upload_to_server(cm.recType, function(progress, fileURLOrError) {
234
                    if (progress === 'ended') { // Insert annotation in text.
235
                        cm.uploadBtn.set('disabled', false);
236
                        cm.insert_annotation(cm.recType, fileURLOrError);
237
                    } else if (progress === 'upload-failed') { // Show error message in upload button.
238
                        cm.uploadBtn.set('disabled', false);
239
                        cm.uploadBtn.set('textContent',
240
                            M.util.get_string('uploadfailed', 'atto_recordrtc') + ' ' + fileURLOrError);
241
                    } else if (progress === 'upload-failed-404') { // 404 error = File too large in Moodle.
242
                        cm.uploadBtn.set('disabled', false);
243
                        cm.uploadBtn.set('textContent', M.util.get_string('uploadfailed404', 'atto_recordrtc'));
244
                    } else if (progress === 'upload-aborted') {
245
                        cm.uploadBtn.set('disabled', false);
246
                        cm.uploadBtn.set('textContent',
247
                            M.util.get_string('uploadaborted', 'atto_recordrtc') + ' ' + fileURLOrError);
248
                    } else {
249
                        cm.uploadBtn.set('textContent', progress);
250
                    }
251
                });
252
            }
253
        });
254
    }
255
};
256