Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c0bd26c
vanila js conversion :: async_process.js
ahmad-arbisoft Apr 18, 2025
a78b195
vanila js conversion :: i18n.js, iterator.js, component.js
ahmad-arbisoft Apr 18, 2025
c5af850
fix: update function calling and definitions
ahmad-arbisoft Apr 21, 2025
5780f4d
vanila js conversion :: html5_video.js
ahmad-arbisoft Apr 21, 2025
f2a0901
vanila js conversion :: video_player.js
ahmad-arbisoft Apr 21, 2025
8512d08
vanila js conversion :: video_full_screen.js
ahmad-arbisoft Apr 21, 2025
d161d81
vanila js conversion :: remove video_progress_slider.js
ahmad-arbisoft Apr 21, 2025
7e823e9
vanila js conversion :: video_progress_slider.js
ahmad-arbisoft Apr 21, 2025
741e98d
vanila js conversion :: video_bumper.js
ahmad-arbisoft Apr 21, 2025
97bc447
vanila js conversion :: sjson.js
ahmad-arbisoft Apr 21, 2025
1e11647
vanila js conversion :: update video_player.js
ahmad-arbisoft Apr 21, 2025
54da71b
vanila js conversion :: update video_player.js
ahmad-arbisoft Apr 22, 2025
bcd6dde
vanila js conversion :: video_auto_advance_control.js
ahmad-arbisoft Apr 22, 2025
1a0fc22
vanila js conversion :: video_auto_advance_control.js
ahmad-arbisoft Apr 22, 2025
010c6cf
vanila js conversion :: html5_hls_video.js
ahmad-arbisoft Apr 22, 2025
5aa6360
vanila js conversion :: video_control.js
ahmad-arbisoft Apr 22, 2025
3212ffd
vanila js conversion :: video_quality_control.js
ahmad-arbisoft Apr 22, 2025
c427b6b
vanila js conversion :: resizer.js
ahmad-arbisoft Apr 22, 2025
18ed4d1
vanila js conversion :: video_completion_handler.js
ahmad-arbisoft Apr 22, 2025
fcb32ba
vanila js conversion :: update video_completion_handler.js
ahmad-arbisoft Apr 22, 2025
bd7f01f
vanila js conversion :: revert video_block_main
ahmad-arbisoft Apr 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions xmodule/assets/video/public/js/async_process.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Provides a convenient way to process a large amount of data without UI blocking.
*
* @param {Array} list - The array to process.
* @param {Function} process - The function to execute on each item.
* @returns {Promise<Array>} - Resolves with the processed array.
*/
export const AsyncProcess = {
array(list, process) {
// Validate input
if (!Array.isArray(list)) {
return Promise.reject(new Error('Input is not an array'));
}

if (typeof process !== 'function' || list.length === 0) {
return Promise.resolve(list);
}

const MAX_DELAY = 50;
const result = [];
let index = 0;
const len = list.length;

const handler = (resolve) => {
const start = Date.now();

while (index < len && Date.now() - start < MAX_DELAY) {
result[index] = process(list[index], index);
index++;
}

if (index < len) {
setTimeout(() => handler(resolve), 25);
} else {
resolve(result);
}
};

return new Promise((resolve) => {
setTimeout(() => handler(resolve), 25);
});
}
};
69 changes: 69 additions & 0 deletions xmodule/assets/video/public/js/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict';

/**
* Creates a new object with the specified prototype object and properties.
* @param {Object} o The object which should be the prototype of the newly-created object.
* @private
* @throws {TypeError, Error}
* @return {Object}
*/
const inherit = Object.create || (function () {
const F = function () { };

return function (o) {
if (arguments.length > 1) {
throw Error('Second argument not supported');
}
if (_.isNull(o) || _.isUndefined(o)) {
throw Error('Cannot set a null [[Prototype]]');
}
if (!_.isObject(o)) {
throw TypeError('Argument must be an object');
}

F.prototype = o;
return new F();
};
}());

/**
* Component constructor function.
* Calls `initialize()` if defined.
* @returns {any}
*/
function Component() {
if ($.isFunction(this.initialize)) {
return this.initialize.apply(this, arguments);
}
}

/**
* Adds an `extend` method to the Component constructor.
* Creates a subclass that inherits from Component.
* @param {Object} protoProps - Prototype methods and properties.
* @param {Object} staticProps - Static methods and properties.
* @returns {Function} Child constructor.
*/
Component.extend = function (protoProps, staticProps) {
const Parent = this;

const Child = function () {
if ($.isFunction(this.initialize)) {
return this.initialize.apply(this, arguments);
}
};

Child.prototype = inherit(Parent.prototype);
Child.constructor = Parent;
Child.__super__ = Parent.prototype;

if (protoProps) {
$.extend(Child.prototype, protoProps);
}

$.extend(Child, Parent, staticProps);

return Child;
};

export { Component };
99 changes: 99 additions & 0 deletions xmodule/assets/video/public/js/html5_hls_video.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { bindAll, once } from 'underscore';
import { Player as HTML5Player } from './02_html5_video.js';
import Hls from 'hls.js';

export class HLSVideoPlayer extends HTML5Player {
constructor(el, config) {
super();

this.config = config;
this.init(el, config);

bindAll(this, 'playVideo', 'pauseVideo', 'onReady');

// Handle unsupported HLS
if (config.HLSOnlySources && !config.canPlayHLS) {
this.showErrorMessage(null, '.video-hls-error');
return;
}

// Setup on initialize
const onInitialize = once(() => {
console.log('[HLS Video]: HLS Player initialized');
this.showPlayButton();
});
config.state.el.addEventListener('initialize', onInitialize);

// Handle native Safari HLS
if (config.browserIsSafari) {
this.videoEl.setAttribute('src', config.videoSources[0]);
} else {
this.hls = new Hls({
autoStartLoad: config.state.auto_advance ?? false
});

this.hls.loadSource(config.videoSources[0]);
this.hls.attachMedia(this.video);

this.hls.on(Hls.Events.ERROR, this.onError.bind(this));

this.hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
console.log('[HLS Video]: MANIFEST_PARSED, qualityLevelsInfo:',
data.levels.map(level => ({
bitrate: level.bitrate,
resolution: `${level.width}x${level.height}`
}))
);
this.config.onReadyHLS?.();
});

this.hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
const level = this.hls.levels[data.level];
console.log('[HLS Video]: LEVEL_SWITCHED, qualityLevelInfo:', {
bitrate: level.bitrate,
resolution: `${level.width}x${level.height}`
});
});
}
}

playVideo() {
super.updatePlayerLoadingState('show');
if (!this.config.browserIsSafari) {
this.hls.startLoad();
}
super.playVideo();
}

pauseVideo() {
super.pauseVideo();
super.updatePlayerLoadingState('hide');
}

onPlaying() {
super.onPlaying();
super.updatePlayerLoadingState('hide');
}

onReady() {
this.config.events.onReady?.(null);
}

onError(event, data) {
if (!data.fatal) return;

switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
console.error('[HLS Video]: Fatal network error. Details:', data.details);
this.hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.error('[HLS Video]: Fatal media error. Details:', data.details);
this.hls.recoverMediaError();
break;
default:
console.error('[HLS Video]: Unrecoverable error. Details:', data.details);
break;
}
}
}
Loading