proctoring 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +245 -0
- data/Rakefile +32 -0
- data/app/assets/config/100ms_manifest.js +1 -0
- data/app/assets/config/knights_watch_manifest.js +4 -0
- data/app/assets/config/kurento_manifest.js +1 -0
- data/app/assets/config/proctoring_manifest.js +3 -0
- data/app/assets/config/videojs_manifest.js +2 -0
- data/app/assets/images/proctoring/poster.png +0 -0
- data/app/assets/javascripts/100ms/examine.js +27 -0
- data/app/assets/javascripts/100ms/hundred_ms.js +143 -0
- data/app/assets/javascripts/100ms/join_proctor_room.js +17 -0
- data/app/assets/javascripts/100ms/proctor.js +152 -0
- data/app/assets/javascripts/kurento/LiveVideoUsingSignalingServer.js +344 -0
- data/app/assets/javascripts/kurento/VideoPlayer.js +63 -0
- data/app/assets/javascripts/kurento/VideoRecording.js +286 -0
- data/app/assets/javascripts/kurento/VideoRecordingUsingSignalingServer.js +224 -0
- data/app/assets/javascripts/kurento/co.js +299 -0
- data/app/assets/javascripts/kurento/kurento-utils.js +4418 -0
- data/app/assets/javascripts/proctoring/stream_channel.js +66 -0
- data/app/assets/javascripts/proctoring/stream_room.js +131 -0
- data/app/assets/javascripts/proctoring/video_recorder.js +172 -0
- data/app/assets/javascripts/videojs/videojs-playlist-ui.js +516 -0
- data/app/assets/javascripts/videojs/videojs-playlist.js +909 -0
- data/app/assets/stylesheets/proctoring/application.css +15 -0
- data/app/assets/stylesheets/proctoring/video_player_100ms.css +34 -0
- data/app/assets/stylesheets/proctoring/video_streamings.css +49 -0
- data/app/assets/stylesheets/scaffold.css +80 -0
- data/app/assets/stylesheets/videojs/videojs-playlist-ui.css +1 -0
- data/app/controllers/proctoring/api/v1/authentication_controller.rb +31 -0
- data/app/controllers/proctoring/api/v1/hundred_ms/services_controller.rb +24 -0
- data/app/controllers/proctoring/application_controller.rb +23 -0
- data/app/controllers/proctoring/video_streamings_controller.rb +108 -0
- data/app/helpers/proctoring/application_helper.rb +4 -0
- data/app/helpers/proctoring/hundred_ms_service_helper.rb +90 -0
- data/app/helpers/proctoring/tokens_helper.rb +55 -0
- data/app/helpers/proctoring/video_streamings_helper.rb +4 -0
- data/app/jobs/proctoring/application_job.rb +4 -0
- data/app/mailers/proctoring/application_mailer.rb +6 -0
- data/app/models/proctoring/application_record.rb +5 -0
- data/app/models/proctoring/video_streaming.rb +54 -0
- data/app/models/proctoring/video_streaming_room.rb +29 -0
- data/app/views/layouts/proctoring/application.html.erb +15 -0
- data/app/views/proctoring/video_player/live_video_proctoring.html.erb +84 -0
- data/app/views/proctoring/video_player/live_video_proctoring_100ms.html.erb +48 -0
- data/app/views/proctoring/video_player/video_player.html.erb +40 -0
- data/app/views/proctoring/video_streamings/_form.html.erb +27 -0
- data/app/views/proctoring/video_streamings/_list.html.erb +27 -0
- data/app/views/proctoring/video_streamings/_record_video_from_client.html.erb +8 -0
- data/app/views/proctoring/video_streamings/_socket_rtc_scripts.html.erb +5 -0
- data/app/views/proctoring/video_streamings/_stream_video.html.erb +8 -0
- data/app/views/proctoring/video_streamings/_video_recording.html.erb +3 -0
- data/app/views/proctoring/video_streamings/_video_recording100ms.html.erb +4 -0
- data/app/views/proctoring/video_streamings/distribute_channel_to_rooms.html.erb +39 -0
- data/app/views/proctoring/video_streamings/edit.html.erb +6 -0
- data/app/views/proctoring/video_streamings/event.html.erb +9 -0
- data/app/views/proctoring/video_streamings/index.html.erb +1 -0
- data/app/views/proctoring/video_streamings/new.html.erb +5 -0
- data/app/views/proctoring/video_streamings/show.html.erb +19 -0
- data/app/views/proctoring/video_streamings/stream_channel.html.erb +1 -0
- data/app/views/proctoring/video_streamings/stream_room.html.erb +1 -0
- data/config/routes.rb +24 -0
- data/db/migrate/20200526061313_create_proctoring_video_streamings.rb +15 -0
- data/db/migrate/20200527045158_create_proctoring_video_streaming_rooms.rb +13 -0
- data/lib/proctoring/engine.rb +41 -0
- data/lib/proctoring/version.rb +3 -0
- data/lib/proctoring.rb +5 -0
- data/lib/tasks/proctoring_tasks.rake +4 -0
- metadata +158 -0
@@ -0,0 +1,909 @@
|
|
1
|
+
/*! @name videojs-playlist @version 4.3.1 @license Apache-2.0 */
|
2
|
+
(function (global, factory) {
|
3
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
|
4
|
+
typeof define === 'function' && define.amd ? define(['video.js'], factory) :
|
5
|
+
(global = global || self, global.videojsPlaylist = factory(global.videojs));
|
6
|
+
}(this, function (videojs) { 'use strict';
|
7
|
+
|
8
|
+
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Validates a number of seconds to use as the auto-advance delay.
|
12
|
+
*
|
13
|
+
* @private
|
14
|
+
* @param {number} s
|
15
|
+
* The number to check
|
16
|
+
*
|
17
|
+
* @return {boolean}
|
18
|
+
* Whether this is a valid second or not
|
19
|
+
*/
|
20
|
+
var validSeconds = function validSeconds(s) {
|
21
|
+
return typeof s === 'number' && !isNaN(s) && s >= 0 && s < Infinity;
|
22
|
+
};
|
23
|
+
/**
|
24
|
+
* Resets the auto-advance behavior of a player.
|
25
|
+
*
|
26
|
+
* @param {Player} player
|
27
|
+
* The player to reset the behavior on
|
28
|
+
*/
|
29
|
+
|
30
|
+
|
31
|
+
var reset = function reset(player) {
|
32
|
+
var aa = player.playlist.autoadvance_;
|
33
|
+
|
34
|
+
if (aa.timeout) {
|
35
|
+
player.clearTimeout(aa.timeout);
|
36
|
+
}
|
37
|
+
|
38
|
+
if (aa.trigger) {
|
39
|
+
player.off('ended', aa.trigger);
|
40
|
+
}
|
41
|
+
|
42
|
+
aa.timeout = null;
|
43
|
+
aa.trigger = null;
|
44
|
+
};
|
45
|
+
/**
|
46
|
+
* Sets up auto-advance behavior on a player.
|
47
|
+
*
|
48
|
+
* @param {Player} player
|
49
|
+
* the current player
|
50
|
+
*
|
51
|
+
* @param {number} delay
|
52
|
+
* The number of seconds to wait before each auto-advance.
|
53
|
+
*
|
54
|
+
* @return {undefined}
|
55
|
+
* Used to short circuit function logic
|
56
|
+
*/
|
57
|
+
|
58
|
+
|
59
|
+
var setup = function setup(player, delay) {
|
60
|
+
reset(player); // Before queuing up new auto-advance behavior, check if `seconds` was
|
61
|
+
// called with a valid value.
|
62
|
+
|
63
|
+
if (!validSeconds(delay)) {
|
64
|
+
player.playlist.autoadvance_.delay = null;
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
|
68
|
+
player.playlist.autoadvance_.delay = delay;
|
69
|
+
|
70
|
+
player.playlist.autoadvance_.trigger = function () {
|
71
|
+
// This calls setup again, which will reset the existing auto-advance and
|
72
|
+
// set up another auto-advance for the next "ended" event.
|
73
|
+
var cancelOnPlay = function cancelOnPlay() {
|
74
|
+
return setup(player, delay);
|
75
|
+
}; // If there is a "play" event while we're waiting for an auto-advance,
|
76
|
+
// we need to cancel the auto-advance. This could mean the user seeked
|
77
|
+
// back into the content or restarted the content. This is reproducible
|
78
|
+
// with an auto-advance > 0.
|
79
|
+
|
80
|
+
|
81
|
+
player.one('play', cancelOnPlay);
|
82
|
+
player.playlist.autoadvance_.timeout = player.setTimeout(function () {
|
83
|
+
reset(player);
|
84
|
+
player.off('play', cancelOnPlay);
|
85
|
+
player.playlist.next();
|
86
|
+
}, delay * 1000);
|
87
|
+
};
|
88
|
+
|
89
|
+
player.one('ended', player.playlist.autoadvance_.trigger);
|
90
|
+
};
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Removes all remote text tracks from a player.
|
94
|
+
*
|
95
|
+
* @param {Player} player
|
96
|
+
* The player to clear tracks on
|
97
|
+
*/
|
98
|
+
|
99
|
+
var clearTracks = function clearTracks(player) {
|
100
|
+
var tracks = player.remoteTextTracks();
|
101
|
+
var i = tracks && tracks.length || 0; // This uses a `while` loop rather than `forEach` because the
|
102
|
+
// `TextTrackList` object is a live DOM list (not an array).
|
103
|
+
|
104
|
+
while (i--) {
|
105
|
+
player.removeRemoteTextTrack(tracks[i]);
|
106
|
+
}
|
107
|
+
};
|
108
|
+
/**
|
109
|
+
* Plays an item on a player's playlist.
|
110
|
+
*
|
111
|
+
* @param {Player} player
|
112
|
+
* The player to play the item on
|
113
|
+
*
|
114
|
+
* @param {Object} item
|
115
|
+
* A source from the playlist.
|
116
|
+
*
|
117
|
+
* @return {Player}
|
118
|
+
* The player that is now playing the item
|
119
|
+
*/
|
120
|
+
|
121
|
+
|
122
|
+
var playItem = function playItem(player, item) {
|
123
|
+
var replay = !player.paused() || player.ended();
|
124
|
+
player.trigger('beforeplaylistitem', item.originalValue || item);
|
125
|
+
|
126
|
+
if (item.playlistItemId_) {
|
127
|
+
player.playlist.currentPlaylistItemId_ = item.playlistItemId_;
|
128
|
+
}
|
129
|
+
|
130
|
+
player.poster(item.poster || '');
|
131
|
+
player.src(item.sources);
|
132
|
+
clearTracks(player);
|
133
|
+
player.ready(function () {
|
134
|
+
(item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player));
|
135
|
+
player.trigger('playlistitem', item.originalValue || item);
|
136
|
+
|
137
|
+
if (replay) {
|
138
|
+
var playPromise = player.play(); // silence error when a pause interrupts a play request
|
139
|
+
// on browsers which return a promise
|
140
|
+
|
141
|
+
if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
|
142
|
+
playPromise.then(null, function (e) {});
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
setup(player, player.playlist.autoadvance_.delay);
|
147
|
+
});
|
148
|
+
return player;
|
149
|
+
};
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Returns whether a playlist item is an object of any kind, excluding null.
|
153
|
+
*
|
154
|
+
* @private
|
155
|
+
*
|
156
|
+
* @param {Object}
|
157
|
+
* value to be checked
|
158
|
+
*
|
159
|
+
* @return {boolean}
|
160
|
+
* The result
|
161
|
+
*/
|
162
|
+
|
163
|
+
var isItemObject = function isItemObject(value) {
|
164
|
+
return !!value && typeof value === 'object';
|
165
|
+
};
|
166
|
+
/**
|
167
|
+
* Look through an array of playlist items and transform any primitive
|
168
|
+
* as well as null values to objects. This method also adds a property
|
169
|
+
* to the transformed item containing original value passed in an input list.
|
170
|
+
*
|
171
|
+
* @private
|
172
|
+
*
|
173
|
+
* @param {Array} arr
|
174
|
+
* An array of playlist items
|
175
|
+
*
|
176
|
+
* @return {Array}
|
177
|
+
* A new array with transformed items
|
178
|
+
*/
|
179
|
+
|
180
|
+
|
181
|
+
var transformPrimitiveItems = function transformPrimitiveItems(arr) {
|
182
|
+
var list = [];
|
183
|
+
var tempItem;
|
184
|
+
arr.forEach(function (item) {
|
185
|
+
if (!isItemObject(item)) {
|
186
|
+
tempItem = Object(item);
|
187
|
+
tempItem.originalValue = item;
|
188
|
+
} else {
|
189
|
+
tempItem = item;
|
190
|
+
}
|
191
|
+
|
192
|
+
list.push(tempItem);
|
193
|
+
});
|
194
|
+
return list;
|
195
|
+
};
|
196
|
+
/**
|
197
|
+
* Generate a unique id for each playlist item object. This id will be used to determine
|
198
|
+
* index of an item in the playlist array for cases where there are multiple items with
|
199
|
+
* the same source set.
|
200
|
+
*
|
201
|
+
* @private
|
202
|
+
*
|
203
|
+
* @param {Array} arr
|
204
|
+
* An array of playlist items
|
205
|
+
*/
|
206
|
+
|
207
|
+
|
208
|
+
var generatePlaylistItemId = function generatePlaylistItemId(arr) {
|
209
|
+
var guid = 1;
|
210
|
+
arr.forEach(function (item) {
|
211
|
+
item.playlistItemId_ = guid++;
|
212
|
+
});
|
213
|
+
};
|
214
|
+
/**
|
215
|
+
* Look through an array of playlist items for a specific playlist item id.
|
216
|
+
*
|
217
|
+
* @private
|
218
|
+
* @param {Array} list
|
219
|
+
* An array of playlist items to look through
|
220
|
+
*
|
221
|
+
* @param {number} currentItemId
|
222
|
+
* The current item ID.
|
223
|
+
*
|
224
|
+
* @return {number}
|
225
|
+
* The index of the playlist item or -1 if not found
|
226
|
+
*/
|
227
|
+
|
228
|
+
|
229
|
+
var indexInPlaylistItemIds = function indexInPlaylistItemIds(list, currentItemId) {
|
230
|
+
for (var i = 0; i < list.length; i++) {
|
231
|
+
if (list[i].playlistItemId_ === currentItemId) {
|
232
|
+
return i;
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
return -1;
|
237
|
+
};
|
238
|
+
/**
|
239
|
+
* Given two sources, check to see whether the two sources are equal.
|
240
|
+
* If both source urls have a protocol, the protocols must match, otherwise, protocols
|
241
|
+
* are ignored.
|
242
|
+
*
|
243
|
+
* @private
|
244
|
+
* @param {string|Object} source1
|
245
|
+
* The first source
|
246
|
+
*
|
247
|
+
* @param {string|Object} source2
|
248
|
+
* The second source
|
249
|
+
*
|
250
|
+
* @return {boolean}
|
251
|
+
* The result
|
252
|
+
*/
|
253
|
+
|
254
|
+
|
255
|
+
var sourceEquals = function sourceEquals(source1, source2) {
|
256
|
+
var src1 = source1;
|
257
|
+
var src2 = source2;
|
258
|
+
|
259
|
+
if (typeof source1 === 'object') {
|
260
|
+
src1 = source1.src;
|
261
|
+
}
|
262
|
+
|
263
|
+
if (typeof source2 === 'object') {
|
264
|
+
src2 = source2.src;
|
265
|
+
}
|
266
|
+
|
267
|
+
if (/^\/\//.test(src1)) {
|
268
|
+
src2 = src2.slice(src2.indexOf('//'));
|
269
|
+
}
|
270
|
+
|
271
|
+
if (/^\/\//.test(src2)) {
|
272
|
+
src1 = src1.slice(src1.indexOf('//'));
|
273
|
+
}
|
274
|
+
|
275
|
+
return src1 === src2;
|
276
|
+
};
|
277
|
+
/**
|
278
|
+
* Look through an array of playlist items for a specific `source`;
|
279
|
+
* checking both the value of elements and the value of their `src`
|
280
|
+
* property.
|
281
|
+
*
|
282
|
+
* @private
|
283
|
+
* @param {Array} arr
|
284
|
+
* An array of playlist items to look through
|
285
|
+
*
|
286
|
+
* @param {string} src
|
287
|
+
* The source to look for
|
288
|
+
*
|
289
|
+
* @return {number}
|
290
|
+
* The index of that source or -1
|
291
|
+
*/
|
292
|
+
|
293
|
+
|
294
|
+
var indexInSources = function indexInSources(arr, src) {
|
295
|
+
for (var i = 0; i < arr.length; i++) {
|
296
|
+
var sources = arr[i].sources;
|
297
|
+
|
298
|
+
if (Array.isArray(sources)) {
|
299
|
+
for (var j = 0; j < sources.length; j++) {
|
300
|
+
var source = sources[j];
|
301
|
+
|
302
|
+
if (source && sourceEquals(source, src)) {
|
303
|
+
return i;
|
304
|
+
}
|
305
|
+
}
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
return -1;
|
310
|
+
};
|
311
|
+
/**
|
312
|
+
* Randomize the contents of an array.
|
313
|
+
*
|
314
|
+
* @private
|
315
|
+
* @param {Array} arr
|
316
|
+
* An array.
|
317
|
+
*
|
318
|
+
* @return {Array}
|
319
|
+
* The same array that was passed in.
|
320
|
+
*/
|
321
|
+
|
322
|
+
|
323
|
+
var randomize = function randomize(arr) {
|
324
|
+
var index = -1;
|
325
|
+
var lastIndex = arr.length - 1;
|
326
|
+
|
327
|
+
while (++index < arr.length) {
|
328
|
+
var rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
|
329
|
+
var value = arr[rand];
|
330
|
+
arr[rand] = arr[index];
|
331
|
+
arr[index] = value;
|
332
|
+
}
|
333
|
+
|
334
|
+
return arr;
|
335
|
+
};
|
336
|
+
/**
|
337
|
+
* Factory function for creating new playlist implementation on the given player.
|
338
|
+
*
|
339
|
+
* API summary:
|
340
|
+
*
|
341
|
+
* playlist(['a', 'b', 'c']) // setter
|
342
|
+
* playlist() // getter
|
343
|
+
* playlist.currentItem() // getter, 0
|
344
|
+
* playlist.currentItem(1) // setter, 1
|
345
|
+
* playlist.next() // 'c'
|
346
|
+
* playlist.previous() // 'b'
|
347
|
+
* playlist.first() // 'a'
|
348
|
+
* playlist.last() // 'c'
|
349
|
+
* playlist.autoadvance(5) // 5 second delay
|
350
|
+
* playlist.autoadvance() // cancel autoadvance
|
351
|
+
*
|
352
|
+
* @param {Player} player
|
353
|
+
* The current player
|
354
|
+
*
|
355
|
+
* @param {Array=} initialList
|
356
|
+
* If given, an initial list of sources with which to populate
|
357
|
+
* the playlist.
|
358
|
+
*
|
359
|
+
* @param {number=} initialIndex
|
360
|
+
* If given, the index of the item in the list that should
|
361
|
+
* be loaded first. If -1, no video is loaded. If omitted, The
|
362
|
+
* the first video is loaded.
|
363
|
+
*
|
364
|
+
* @return {Function}
|
365
|
+
* Returns the playlist function specific to the given player.
|
366
|
+
*/
|
367
|
+
|
368
|
+
|
369
|
+
function factory(player, initialList, initialIndex) {
|
370
|
+
if (initialIndex === void 0) {
|
371
|
+
initialIndex = 0;
|
372
|
+
}
|
373
|
+
|
374
|
+
var list = null;
|
375
|
+
var changing = false;
|
376
|
+
/**
|
377
|
+
* Get/set the playlist for a player.
|
378
|
+
*
|
379
|
+
* This function is added as an own property of the player and has its
|
380
|
+
* own methods which can be called to manipulate the internal state.
|
381
|
+
*
|
382
|
+
* @param {Array} [newList]
|
383
|
+
* If given, a new list of sources with which to populate the
|
384
|
+
* playlist. Without this, the function acts as a getter.
|
385
|
+
*
|
386
|
+
* @param {number} [newIndex]
|
387
|
+
* If given, the index of the item in the list that should
|
388
|
+
* be loaded first. If -1, no video is loaded. If omitted, The
|
389
|
+
* the first video is loaded.
|
390
|
+
*
|
391
|
+
* @return {Array}
|
392
|
+
* The playlist
|
393
|
+
*/
|
394
|
+
|
395
|
+
var playlist = player.playlist = function (newList, newIndex) {
|
396
|
+
if (newIndex === void 0) {
|
397
|
+
newIndex = 0;
|
398
|
+
}
|
399
|
+
|
400
|
+
if (changing) {
|
401
|
+
throw new Error('do not call playlist() during a playlist change');
|
402
|
+
}
|
403
|
+
|
404
|
+
if (Array.isArray(newList)) {
|
405
|
+
// @todo - Simplify this to `list.slice()` for v5.
|
406
|
+
var previousPlaylist = Array.isArray(list) ? list.slice() : null;
|
407
|
+
var nextPlaylist = newList.slice();
|
408
|
+
list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects
|
409
|
+
|
410
|
+
if (list.filter(function (item) {
|
411
|
+
return isItemObject(item);
|
412
|
+
}).length !== list.length) {
|
413
|
+
list = transformPrimitiveItems(list);
|
414
|
+
} // Add unique id to each playlist item. This id will be used
|
415
|
+
// to determine index in cases where there are more than one
|
416
|
+
// identical sources in the playlist.
|
417
|
+
|
418
|
+
|
419
|
+
generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle.
|
420
|
+
|
421
|
+
changing = true;
|
422
|
+
player.trigger({
|
423
|
+
type: 'duringplaylistchange',
|
424
|
+
nextIndex: newIndex,
|
425
|
+
nextPlaylist: nextPlaylist,
|
426
|
+
previousIndex: playlist.currentIndex_,
|
427
|
+
// @todo - Simplify this to simply pass along `previousPlaylist` for v5.
|
428
|
+
previousPlaylist: previousPlaylist || []
|
429
|
+
});
|
430
|
+
changing = false;
|
431
|
+
|
432
|
+
if (newIndex !== -1) {
|
433
|
+
playlist.currentItem(newIndex);
|
434
|
+
} // The only time the previous playlist is null is the first call to this
|
435
|
+
// function. This allows us to fire the `duringplaylistchange` event
|
436
|
+
// every time the playlist is populated and to maintain backward
|
437
|
+
// compatibility by not firing the `playlistchange` event on the initial
|
438
|
+
// population of the list.
|
439
|
+
//
|
440
|
+
// @todo - Remove this condition in preparation for v5.
|
441
|
+
|
442
|
+
|
443
|
+
if (previousPlaylist) {
|
444
|
+
player.setTimeout(function () {
|
445
|
+
player.trigger('playlistchange');
|
446
|
+
}, 0);
|
447
|
+
}
|
448
|
+
} // Always return a shallow clone of the playlist list.
|
449
|
+
// We also want to return originalValue if any item in the list has it.
|
450
|
+
|
451
|
+
|
452
|
+
return list.map(function (item) {
|
453
|
+
return item.originalValue || item;
|
454
|
+
}).slice();
|
455
|
+
}; // On a new source, if there is no current item, disable auto-advance.
|
456
|
+
|
457
|
+
|
458
|
+
player.on('loadstart', function () {
|
459
|
+
if (playlist.currentItem() === -1) {
|
460
|
+
reset(player);
|
461
|
+
}
|
462
|
+
});
|
463
|
+
playlist.currentIndex_ = -1;
|
464
|
+
playlist.player_ = player;
|
465
|
+
playlist.autoadvance_ = {};
|
466
|
+
playlist.repeat_ = false;
|
467
|
+
playlist.currentPlaylistItemId_ = null;
|
468
|
+
/**
|
469
|
+
* Get or set the current item in the playlist.
|
470
|
+
*
|
471
|
+
* During the duringplaylistchange event, acts only as a getter.
|
472
|
+
*
|
473
|
+
* @param {number} [index]
|
474
|
+
* If given as a valid value, plays the playlist item at that index.
|
475
|
+
*
|
476
|
+
* @return {number}
|
477
|
+
* The current item index.
|
478
|
+
*/
|
479
|
+
|
480
|
+
playlist.currentItem = function (index) {
|
481
|
+
// If the playlist is changing, only act as a getter.
|
482
|
+
if (changing) {
|
483
|
+
return playlist.currentIndex_;
|
484
|
+
} // Act as a setter when the index is given and is a valid number.
|
485
|
+
|
486
|
+
|
487
|
+
if (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) {
|
488
|
+
playlist.currentIndex_ = index;
|
489
|
+
playItem(playlist.player_, list[playlist.currentIndex_]);
|
490
|
+
return playlist.currentIndex_;
|
491
|
+
}
|
492
|
+
|
493
|
+
var src = playlist.player_.currentSrc() || ''; // If there is a currentPlaylistItemId_, validate that it matches the
|
494
|
+
// current source URL returned by the player. This is sufficient evidence
|
495
|
+
// to suggest that the source was set by the playlist plugin. This code
|
496
|
+
// exists primarily to deal with playlists where multiple items have the
|
497
|
+
// same source.
|
498
|
+
|
499
|
+
if (playlist.currentPlaylistItemId_) {
|
500
|
+
var indexInItemIds = indexInPlaylistItemIds(list, playlist.currentPlaylistItemId_);
|
501
|
+
var item = list[indexInItemIds]; // Found a match, this is our current index!
|
502
|
+
|
503
|
+
if (item && Array.isArray(item.sources) && indexInSources([item], src) > -1) {
|
504
|
+
playlist.currentIndex_ = indexInItemIds;
|
505
|
+
return playlist.currentIndex_;
|
506
|
+
} // If this does not match the current source, null it out so subsequent
|
507
|
+
// calls can skip this step.
|
508
|
+
|
509
|
+
|
510
|
+
playlist.currentPlaylistItemId_ = null;
|
511
|
+
} // Finally, if we don't have a valid, current playlist item ID, we can
|
512
|
+
// auto-detect it based on the player's current source URL.
|
513
|
+
|
514
|
+
|
515
|
+
playlist.currentIndex_ = playlist.indexOf(src);
|
516
|
+
return playlist.currentIndex_;
|
517
|
+
};
|
518
|
+
/**
|
519
|
+
* Checks if the playlist contains a value.
|
520
|
+
*
|
521
|
+
* @param {string|Object|Array} value
|
522
|
+
* The value to check
|
523
|
+
*
|
524
|
+
* @return {boolean}
|
525
|
+
* The result
|
526
|
+
*/
|
527
|
+
|
528
|
+
|
529
|
+
playlist.contains = function (value) {
|
530
|
+
return playlist.indexOf(value) !== -1;
|
531
|
+
};
|
532
|
+
/**
|
533
|
+
* Gets the index of a value in the playlist or -1 if not found.
|
534
|
+
*
|
535
|
+
* @param {string|Object|Array} value
|
536
|
+
* The value to find the index of
|
537
|
+
*
|
538
|
+
* @return {number}
|
539
|
+
* The index or -1
|
540
|
+
*/
|
541
|
+
|
542
|
+
|
543
|
+
playlist.indexOf = function (value) {
|
544
|
+
if (typeof value === 'string') {
|
545
|
+
return indexInSources(list, value);
|
546
|
+
}
|
547
|
+
|
548
|
+
var sources = Array.isArray(value) ? value : value.sources;
|
549
|
+
|
550
|
+
for (var i = 0; i < sources.length; i++) {
|
551
|
+
var source = sources[i];
|
552
|
+
|
553
|
+
if (typeof source === 'string') {
|
554
|
+
return indexInSources(list, source);
|
555
|
+
} else if (source.src) {
|
556
|
+
return indexInSources(list, source.src);
|
557
|
+
}
|
558
|
+
}
|
559
|
+
|
560
|
+
return -1;
|
561
|
+
};
|
562
|
+
/**
|
563
|
+
* Get the index of the current item in the playlist. This is identical to
|
564
|
+
* calling `currentItem()` with no arguments.
|
565
|
+
*
|
566
|
+
* @return {number}
|
567
|
+
* The current item index.
|
568
|
+
*/
|
569
|
+
|
570
|
+
|
571
|
+
playlist.currentIndex = function () {
|
572
|
+
return playlist.currentItem();
|
573
|
+
};
|
574
|
+
/**
|
575
|
+
* Get the index of the last item in the playlist.
|
576
|
+
*
|
577
|
+
* @return {number}
|
578
|
+
* The index of the last item in the playlist or -1 if there are no
|
579
|
+
* items.
|
580
|
+
*/
|
581
|
+
|
582
|
+
|
583
|
+
playlist.lastIndex = function () {
|
584
|
+
return list.length - 1;
|
585
|
+
};
|
586
|
+
/**
|
587
|
+
* Get the index of the next item in the playlist.
|
588
|
+
*
|
589
|
+
* @return {number}
|
590
|
+
* The index of the next item in the playlist or -1 if there is no
|
591
|
+
* current item.
|
592
|
+
*/
|
593
|
+
|
594
|
+
|
595
|
+
playlist.nextIndex = function () {
|
596
|
+
var current = playlist.currentItem();
|
597
|
+
|
598
|
+
if (current === -1) {
|
599
|
+
return -1;
|
600
|
+
}
|
601
|
+
|
602
|
+
var lastIndex = playlist.lastIndex(); // When repeating, loop back to the beginning on the last item.
|
603
|
+
|
604
|
+
if (playlist.repeat_ && current === lastIndex) {
|
605
|
+
return 0;
|
606
|
+
} // Don't go past the end of the playlist.
|
607
|
+
|
608
|
+
|
609
|
+
return Math.min(current + 1, lastIndex);
|
610
|
+
};
|
611
|
+
/**
|
612
|
+
* Get the index of the previous item in the playlist.
|
613
|
+
*
|
614
|
+
* @return {number}
|
615
|
+
* The index of the previous item in the playlist or -1 if there is
|
616
|
+
* no current item.
|
617
|
+
*/
|
618
|
+
|
619
|
+
|
620
|
+
playlist.previousIndex = function () {
|
621
|
+
var current = playlist.currentItem();
|
622
|
+
|
623
|
+
if (current === -1) {
|
624
|
+
return -1;
|
625
|
+
} // When repeating, loop back to the end of the playlist.
|
626
|
+
|
627
|
+
|
628
|
+
if (playlist.repeat_ && current === 0) {
|
629
|
+
return playlist.lastIndex();
|
630
|
+
} // Don't go past the beginning of the playlist.
|
631
|
+
|
632
|
+
|
633
|
+
return Math.max(current - 1, 0);
|
634
|
+
};
|
635
|
+
/**
|
636
|
+
* Plays the first item in the playlist.
|
637
|
+
*
|
638
|
+
* @return {Object|undefined}
|
639
|
+
* Returns undefined and has no side effects if the list is empty.
|
640
|
+
*/
|
641
|
+
|
642
|
+
|
643
|
+
playlist.first = function () {
|
644
|
+
if (changing) {
|
645
|
+
return;
|
646
|
+
}
|
647
|
+
|
648
|
+
var newItem = playlist.currentItem(0);
|
649
|
+
|
650
|
+
if (list.length) {
|
651
|
+
return list[newItem].originalValue || list[newItem];
|
652
|
+
}
|
653
|
+
|
654
|
+
playlist.currentIndex_ = -1;
|
655
|
+
};
|
656
|
+
/**
|
657
|
+
* Plays the last item in the playlist.
|
658
|
+
*
|
659
|
+
* @return {Object|undefined}
|
660
|
+
* Returns undefined and has no side effects if the list is empty.
|
661
|
+
*/
|
662
|
+
|
663
|
+
|
664
|
+
playlist.last = function () {
|
665
|
+
if (changing) {
|
666
|
+
return;
|
667
|
+
}
|
668
|
+
|
669
|
+
var newItem = playlist.currentItem(playlist.lastIndex());
|
670
|
+
|
671
|
+
if (list.length) {
|
672
|
+
return list[newItem].originalValue || list[newItem];
|
673
|
+
}
|
674
|
+
|
675
|
+
playlist.currentIndex_ = -1;
|
676
|
+
};
|
677
|
+
/**
|
678
|
+
* Plays the next item in the playlist.
|
679
|
+
*
|
680
|
+
* @return {Object|undefined}
|
681
|
+
* Returns undefined and has no side effects if on last item.
|
682
|
+
*/
|
683
|
+
|
684
|
+
|
685
|
+
playlist.next = function () {
|
686
|
+
if (changing) {
|
687
|
+
return;
|
688
|
+
}
|
689
|
+
|
690
|
+
var index = playlist.nextIndex();
|
691
|
+
|
692
|
+
if (index !== playlist.currentIndex_) {
|
693
|
+
var newItem = playlist.currentItem(index);
|
694
|
+
return list[newItem].originalValue || list[newItem];
|
695
|
+
}
|
696
|
+
};
|
697
|
+
/**
|
698
|
+
* Plays the previous item in the playlist.
|
699
|
+
*
|
700
|
+
* @return {Object|undefined}
|
701
|
+
* Returns undefined and has no side effects if on first item.
|
702
|
+
*/
|
703
|
+
|
704
|
+
|
705
|
+
playlist.previous = function () {
|
706
|
+
if (changing) {
|
707
|
+
return;
|
708
|
+
}
|
709
|
+
|
710
|
+
var index = playlist.previousIndex();
|
711
|
+
|
712
|
+
if (index !== playlist.currentIndex_) {
|
713
|
+
var newItem = playlist.currentItem(index);
|
714
|
+
return list[newItem].originalValue || list[newItem];
|
715
|
+
}
|
716
|
+
};
|
717
|
+
/**
|
718
|
+
* Set up auto-advance on the playlist.
|
719
|
+
*
|
720
|
+
* @param {number} [delay]
|
721
|
+
* The number of seconds to wait before each auto-advance.
|
722
|
+
*/
|
723
|
+
|
724
|
+
|
725
|
+
playlist.autoadvance = function (delay) {
|
726
|
+
setup(playlist.player_, delay);
|
727
|
+
};
|
728
|
+
/**
|
729
|
+
* Sets `repeat` option, which makes the "next" video of the last video in
|
730
|
+
* the playlist be the first video in the playlist.
|
731
|
+
*
|
732
|
+
* @param {boolean} [val]
|
733
|
+
* The value to set repeat to
|
734
|
+
*
|
735
|
+
* @return {boolean}
|
736
|
+
* The current value of repeat
|
737
|
+
*/
|
738
|
+
|
739
|
+
|
740
|
+
playlist.repeat = function (val) {
|
741
|
+
if (val === undefined) {
|
742
|
+
return playlist.repeat_;
|
743
|
+
}
|
744
|
+
|
745
|
+
if (typeof val !== 'boolean') {
|
746
|
+
videojs.log.error('videojs-playlist: Invalid value for repeat', val);
|
747
|
+
return;
|
748
|
+
}
|
749
|
+
|
750
|
+
playlist.repeat_ = !!val;
|
751
|
+
return playlist.repeat_;
|
752
|
+
};
|
753
|
+
/**
|
754
|
+
* Sorts the playlist array.
|
755
|
+
*
|
756
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort}
|
757
|
+
* @fires playlistsorted
|
758
|
+
*
|
759
|
+
* @param {Function} compare
|
760
|
+
* A comparator function as per the native Array method.
|
761
|
+
*/
|
762
|
+
|
763
|
+
|
764
|
+
playlist.sort = function (compare) {
|
765
|
+
// Bail if the array is empty.
|
766
|
+
if (!list.length) {
|
767
|
+
return;
|
768
|
+
}
|
769
|
+
|
770
|
+
list.sort(compare); // If the playlist is changing, don't trigger events.
|
771
|
+
|
772
|
+
if (changing) {
|
773
|
+
return;
|
774
|
+
}
|
775
|
+
/**
|
776
|
+
* Triggered after the playlist is sorted internally.
|
777
|
+
*
|
778
|
+
* @event playlistsorted
|
779
|
+
* @type {Object}
|
780
|
+
*/
|
781
|
+
|
782
|
+
|
783
|
+
player.trigger('playlistsorted');
|
784
|
+
};
|
785
|
+
/**
|
786
|
+
* Reverses the playlist array.
|
787
|
+
*
|
788
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse}
|
789
|
+
* @fires playlistsorted
|
790
|
+
*/
|
791
|
+
|
792
|
+
|
793
|
+
playlist.reverse = function () {
|
794
|
+
// Bail if the array is empty.
|
795
|
+
if (!list.length) {
|
796
|
+
return;
|
797
|
+
}
|
798
|
+
|
799
|
+
list.reverse(); // If the playlist is changing, don't trigger events.
|
800
|
+
|
801
|
+
if (changing) {
|
802
|
+
return;
|
803
|
+
}
|
804
|
+
/**
|
805
|
+
* Triggered after the playlist is sorted internally.
|
806
|
+
*
|
807
|
+
* @event playlistsorted
|
808
|
+
* @type {Object}
|
809
|
+
*/
|
810
|
+
|
811
|
+
|
812
|
+
player.trigger('playlistsorted');
|
813
|
+
};
|
814
|
+
/**
|
815
|
+
* Shuffle the contents of the list randomly.
|
816
|
+
*
|
817
|
+
* @see {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js}
|
818
|
+
* @fires playlistsorted
|
819
|
+
* @todo Make the `rest` option default to `true` in v5.0.0.
|
820
|
+
* @param {Object} [options]
|
821
|
+
* An object containing shuffle options.
|
822
|
+
*
|
823
|
+
* @param {boolean} [options.rest = false]
|
824
|
+
* By default, the entire playlist is randomized. However, this may
|
825
|
+
* not be desirable in all cases, such as when a user is already
|
826
|
+
* watching a video.
|
827
|
+
*
|
828
|
+
* When `true` is passed for this option, it will only shuffle
|
829
|
+
* playlist items after the current item. For example, when on the
|
830
|
+
* first item, will shuffle the second item and beyond.
|
831
|
+
*/
|
832
|
+
|
833
|
+
|
834
|
+
playlist.shuffle = function (_temp) {
|
835
|
+
var _ref = _temp === void 0 ? {} : _temp,
|
836
|
+
rest = _ref.rest;
|
837
|
+
|
838
|
+
var index = 0;
|
839
|
+
var arr = list; // When options.rest is true, start randomization at the item after the
|
840
|
+
// current item.
|
841
|
+
|
842
|
+
if (rest) {
|
843
|
+
index = playlist.currentIndex_ + 1;
|
844
|
+
arr = list.slice(index);
|
845
|
+
} // Bail if the array is empty or too short to shuffle.
|
846
|
+
|
847
|
+
|
848
|
+
if (arr.length <= 1) {
|
849
|
+
return;
|
850
|
+
}
|
851
|
+
|
852
|
+
randomize(arr); // When options.rest is true, splice the randomized sub-array back into
|
853
|
+
// the original array.
|
854
|
+
|
855
|
+
if (rest) {
|
856
|
+
var _list;
|
857
|
+
|
858
|
+
(_list = list).splice.apply(_list, [index, arr.length].concat(arr));
|
859
|
+
} // If the playlist is changing, don't trigger events.
|
860
|
+
|
861
|
+
|
862
|
+
if (changing) {
|
863
|
+
return;
|
864
|
+
}
|
865
|
+
/**
|
866
|
+
* Triggered after the playlist is sorted internally.
|
867
|
+
*
|
868
|
+
* @event playlistsorted
|
869
|
+
* @type {Object}
|
870
|
+
*/
|
871
|
+
|
872
|
+
|
873
|
+
player.trigger('playlistsorted');
|
874
|
+
}; // If an initial list was given, populate the playlist with it.
|
875
|
+
|
876
|
+
|
877
|
+
if (Array.isArray(initialList)) {
|
878
|
+
playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array.
|
879
|
+
} else {
|
880
|
+
list = [];
|
881
|
+
}
|
882
|
+
|
883
|
+
return playlist;
|
884
|
+
}
|
885
|
+
|
886
|
+
var version = "4.3.1";
|
887
|
+
|
888
|
+
var registerPlugin = videojs.registerPlugin || videojs.plugin;
|
889
|
+
/**
|
890
|
+
* The video.js playlist plugin. Invokes the playlist-maker to create a
|
891
|
+
* playlist function on the specific player.
|
892
|
+
*
|
893
|
+
* @param {Array} list
|
894
|
+
* a list of sources
|
895
|
+
*
|
896
|
+
* @param {number} item
|
897
|
+
* The index to start at
|
898
|
+
*/
|
899
|
+
|
900
|
+
var plugin = function plugin(list, item) {
|
901
|
+
factory(this, list, item);
|
902
|
+
};
|
903
|
+
|
904
|
+
registerPlugin('playlist', plugin);
|
905
|
+
plugin.VERSION = version;
|
906
|
+
|
907
|
+
return plugin;
|
908
|
+
|
909
|
+
}));
|