Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

/**
 * videojs-ogvjs
 * @version 1.0.0
 * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
 * @license MIT
 */
define(['media_videojs/video-lazy', './local/ogv/ogv'], (function (videojs, ogv) { 'use strict';

        // We can access public classes either as ogv.OGVPlayer or just OGVPlayer.
        // But ogv.OGVPlayer will make the lint tools happier.
        const OGVCompat = ogv.OGVCompat;
        const OGVLoader = ogv.OGVLoader;
        const OGVPlayer = ogv.OGVPlayer;
        const Tech = videojs.getComponent('Tech');

        const androidOS = 'Android';
        const iPhoneOS = 'iPhoneOS';
        const iPadOS = 'iPadOS';
        const otherOS = 'Other';

        /**
         * Object.defineProperty but "lazy", which means that the value is only set after
         * it retrieved the first time, rather than being set right away.
         *
         * @param {Object} obj the object to set the property on.
         * @param {string} key the key for the property to set.
         * @param {Function} getValue the function used to get the value when it is needed.
         * @param {boolean} setter whether a setter should be allowed or not.
         */
        const defineLazyProperty = (obj, key, getValue, setter = true) => {
                const set = (value) => {
                        Object.defineProperty(obj, key, {value, enumerable: true, writable: true});
                };

                const options = {
                        configurable: true,
                        enumerable: true,
                        get() {
                                const value = getValue();

                                set(value);
                                return value;
                        }
                };

                if (setter) {
                        options.set = set;
                }

                return Object.defineProperty(obj, key, options);
        };

        /**
         * Get the device's OS.
         *
         * @return {string} Device's OS.
         */
        const getDeviceOS = () => {
                /* global navigator */
                const ua = navigator.userAgent;

                if (/android/i.test(ua)) {
                        return androidOS;
                } else if (/iPad|iPhone|iPod/.test(ua)) {
                        return iPhoneOS;
                } else if ((navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) {
                        return iPadOS;
                }
                return otherOS;
        };

        /**
         * OgvJS Media Controller - Wrapper for ogv.js Media API
         *
         * @mixes Tech~SourceHandlerAdditions
         * @extends Tech
         */
        class OgvJS extends Tech {

                /**
                 * Create an instance of this Tech.
                 *
                 * @param {Object} [options] The key/value store of player options.
                 * @param {Component~ReadyCallback} ready Callback function to call when the `OgvJS` Tech is ready.
                 */
                constructor(options, ready) {
                        super(options, ready);

                        this.el_.src = options.source.src;
                        OgvJS.setIfAvailable(this.el_, 'autoplay', options.autoplay);
                        OgvJS.setIfAvailable(this.el_, 'loop', options.loop);
                        OgvJS.setIfAvailable(this.el_, 'poster', options.poster);
                        OgvJS.setIfAvailable(this.el_, 'preload', options.preload);

                        this.on('loadedmetadata', () => {
                                if (getDeviceOS() === iPhoneOS) {
                                        // iPhoneOS add some inline styles to the canvas, we need to remove it.
                                        const canvas = this.el_.getElementsByTagName('canvas')[0];

                                        canvas.style.removeProperty('width');
                                        canvas.style.removeProperty('margin');
                                }
                        });

                        this.triggerReady();
                }

                /**
                 * Create the 'OgvJS' Tech's DOM element.
                 *
                 * @return {Element} The element that gets created.
                 */
                createEl() {
                        const options = this.options_;

                        if (options.base) {
                                OGVLoader.base = options.base;
                        } else {
                                throw new Error('Please specify the base for the ogv.js library');
                        }

                        const el = new OGVPlayer(options);

                        el.className += ' vjs-tech';
                        options.tag = el;

                        return el;
                }

                /**
                 * Start playback
                 *
                 * @method play
                 */
                play() {
                        this.el_.play();
                }

                /**
                 * Get the current playback speed.
                 *
                 * @return {number}
                 * @method playbackRate
                 */
                playbackRate() {
                        return this.el_.playbackRate || 1;
                }

                /**
                 * Set the playback speed.
                 *
                 * @param {number} val Speed for the player to play.
                 * @method setPlaybackRate
                 */
                setPlaybackRate(val) {
                        if (this.el_.hasOwnProperty('playbackRate')) {
                                this.el_.playbackRate = val;
                        }
                }

                /**
                 * Returns a TimeRanges object that represents the ranges of the media resource that the user agent has played.
                 *
                 * @return {TimeRangeObject} the range of points on the media timeline that has been reached through normal playback
                 */
                played() {
                        return this.el_.played;
                }

                /**
                 * Pause playback
                 *
                 * @method pause
                 */
                pause() {
                        this.el_.pause();
                }

                /**
                 * Is the player paused or not.
                 *
                 * @return {boolean}
                 * @method paused
                 */
                paused() {
                        return this.el_.paused;
                }

                /**
                 * Get current playing time.
                 *
                 * @return {number}
                 * @method currentTime
                 */
                currentTime() {
                        return this.el_.currentTime;
                }

                /**
                 * Set current playing time.
                 *
                 * @param {number} seconds Current time of audio/video.
                 * @method setCurrentTime
                 */
                setCurrentTime(seconds) {
                        try {
                                this.el_.currentTime = seconds;
                        } catch (e) {
                                videojs.log(e, 'Media is not ready. (Video.JS)');
                        }
                }

                /**
                 * Get media's duration.
                 *
                 * @return {number}
                 * @method duration
                 */
                duration() {
                        if (this.el_.duration && this.el_.duration !== Infinity) {
                                return this.el_.duration;
                        }

                        return 0;
                }

                /**
                 * Get a TimeRange object that represents the intersection
                 * of the time ranges for which the user agent has all
                 * relevant media.
                 *
                 * @return {TimeRangeObject}
                 * @method buffered
                 */
                buffered() {
                        return this.el_.buffered;
                }

                /**
                 * Get current volume level.
                 *
                 * @return {number}
                 * @method volume
                 */
                volume() {
                        return this.el_.hasOwnProperty('volume') ? this.el_.volume : 1;
                }

                /**
                 * Set current playing volume level.
                 *
                 * @param {number} percentAsDecimal Volume percent as a decimal.
                 * @method setVolume
                 */
                setVolume(percentAsDecimal) {
                        // Apple does not allow iOS and iPadOS devices to set the volume on UI.
                        if (getDeviceOS() !== iPhoneOS && getDeviceOS() !== iPadOS && this.el_.hasOwnProperty('volume')) {
                                this.el_.volume = percentAsDecimal;
                        }
                }

                /**
                 * Is the player muted or not.
                 *
                 * @return {boolean}
                 * @method muted
                 */
                muted() {
                        return this.el_.muted;
                }

                /**
                 * Mute the player.
                 *
                 * @param {boolean} muted True to mute the player.
                 */
                setMuted(muted) {
                        this.el_.muted = !!muted;
                }

                /**
                 * Is the player muted by default or not.
                 *
                 * @return {boolean}
                 * @method defaultMuted
                 */
                defaultMuted() {
                        return this.el_.defaultMuted || false;
                }

                /**
                 * Get the player width.
                 *
                 * @return {number}
                 * @method width
                 */
                width() {
                        return this.el_.offsetWidth;
                }

                /**
                 * Get the player height.
                 *
                 * @return {number}
                 * @method height
                 */
                height() {
                        return this.el_.offsetHeight;
                }

                /**
                 * Get the video width.
                 *
                 * @return {number}
                 * @method videoWidth
                 */
                videoWidth() {
                        return this.el_.videoWidth;
                }

                /**
                 * Get the video height.
                 *
                 * @return {number}
                 * @method videoHeight
                 */
                videoHeight() {
                        return this.el_.videoHeight;
                }

                /**
                 * Get/set media source.
                 *
                 * @param {Object=} src Source object
                 * @return {Object}
                 * @method src
                 */
                src(src) {
                        if (typeof src === 'undefined') {
                                return this.el_.src;
                        }
                        this.el_.src = src;
                }

                /**
                 * Load the media into the player.
                 *
                 * @method load
                 */
                load() {
                        this.el_.load();
                }

                /**
                 * Get current media source.
                 *
                 * @return {Object}
                 * @method currentSrc
                 */
                currentSrc() {
                        if (this.currentSource_) {
                                return this.currentSource_.src;
                        }
                        return this.el_.currentSrc;
                }

                /**
                 * Get media poster URL.
                 *
                 * @return {string}
                 * @method poster
                 */
                poster() {
                        return this.el_.poster;
                }

                /**
                 * Set media poster URL.
                 *
                 * @param {string} url the poster image's url.
                 * @method
                 */
                setPoster(url) {
                        this.el_.poster = url;
                }

                /**
                 * Is the media preloaded or not.
                 *
                 * @return {string}
                 * @method preload
                 */
                preload() {
                        return this.el_.preload || 'none';
                }

                /**
                 * Set the media preload method.
                 *
                 * @param {string} val Value for preload attribute.
                 * @method setPreload
                 */
                setPreload(val) {
                        if (this.el_.hasOwnProperty('preload')) {
                                this.el_.preload = val;
                        }
                }

                /**
                 * Is the media auto-played or not.
                 *
                 * @return {boolean}
                 * @method autoplay
                 */
                autoplay() {
                        return this.el_.autoplay || false;
                }

                /**
                 * Set media autoplay method.
                 *
                 * @param {boolean} val Value for autoplay attribute.
                 * @method setAutoplay
                 */
                setAutoplay(val) {
                        if (this.el_.hasOwnProperty('autoplay')) {
                                this.el_.autoplay = !!val;
                        }
                }

                /**
                 * Does the media has controls or not.
                 *
                 * @return {boolean}
                 * @method controls
                 */
                controls() {
                        return this.el_.controls || false;
                }

                /**
                 * Set the media controls method.
                 *
                 * @param {boolean} val Value for controls attribute.
                 * @method setControls
                 */
                setControls(val) {
                        if (this.el_.hasOwnProperty('controls')) {
                                this.el_.controls = !!val;
                        }
                }

                /**
                 * Is the media looped or not.
                 *
                 * @return {boolean}
                 * @method loop
                 */
                loop() {
                        return this.el_.loop || false;
                }

                /**
                 * Set the media loop method.
                 *
                 * @param {boolean} val Value for loop attribute.
                 * @method setLoop
                 */
                setLoop(val) {
                        if (this.el_.hasOwnProperty('loop')) {
                                this.el_.loop = !!val;
                        }
                }

                /**
                 * Get a TimeRanges object that represents the
                 * ranges of the media resource to which it is possible
                 * for the user agent to seek.
                 *
                 * @return {TimeRangeObject}
                 * @method seekable
                 */
                seekable() {
                        return this.el_.seekable;
                }

                /**
                 * Is player in the "seeking" state or not.
                 *
                 * @return {boolean}
                 * @method seeking
                 */
                seeking() {
                        return this.el_.seeking;
                }

                /**
                 * Is the media ended or not.
                 *
                 * @return {boolean}
                 * @method ended
                 */
                ended() {
                        return this.el_.ended;
                }

                /**
                 * Get the current state of network activity
                 * NETWORK_EMPTY (numeric value 0)
                 * NETWORK_IDLE (numeric value 1)
                 * NETWORK_LOADING (numeric value 2)
                 * NETWORK_NO_SOURCE (numeric value 3)
                 *
                 * @return {number}
                 * @method networkState
                 */
                networkState() {
                        return this.el_.networkState;
                }

                /**
                 * Get the current state of the player.
                 * HAVE_NOTHING (numeric value 0)
                 * HAVE_METADATA (numeric value 1)
                 * HAVE_CURRENT_DATA (numeric value 2)
                 * HAVE_FUTURE_DATA (numeric value 3)
                 * HAVE_ENOUGH_DATA (numeric value 4)
                 *
                 * @return {number}
                 * @method readyState
                 */
                readyState() {
                        return this.el_.readyState;
                }

                /**
                 * Does the player support native fullscreen mode or not. (Mobile devices)
                 *
                 * @return {boolean}
                 */
                supportsFullScreen() {
                        // iOS devices have some problem with HTML5 fullscreen api so we need to fallback to fullWindow mode.
                        return false;
                }

                /**
                 * Get media player error.
                 *
                 * @return {string}
                 * @method error
                 */
                error() {
                        return this.el_.error;
                }

        }

        /**
         * List of available events of the media player.
         *
         * @private
         * @type {Array}
         */
        OgvJS.Events = [
                'loadstart',
                'suspend',
                'abort',
                'error',
                'emptied',
                'stalled',
                'loadedmetadata',
                'loadeddata',
                'canplay',
                'canplaythrough',
                'playing',
                'waiting',
                'seeking',
                'seeked',
                'ended',
                'durationchange',
                'timeupdate',
                'progress',
                'play',
                'pause',
                'ratechange',
                'resize',
                'volumechange'
        ];

        /**
         * Set the value for the player is it has that property.
         *
         * @param {Element} el
         * @param {string} name
         * @param value
         */
        OgvJS.setIfAvailable = (el, name, value) => {
                if (el.hasOwnProperty(name)) {
                        el[name] = value;
                }
        };

        /**
         * Check if browser/device is supported by Ogv.JS.
         *
         * @return {boolean}
         */
        OgvJS.isSupported = () => {
                return OGVCompat.supported('OGVPlayer');
        };

        /**
         * Check if the tech can support the given type.
         *
         * @param {string} type The mimetype to check
         * @return {string} 'probably', 'maybe', or '' (empty string)
         */
        OgvJS.canPlayType = (type) => {
                return (type.indexOf('/ogg') !== -1 || type.indexOf('/webm')) ? 'maybe' : '';
        };

        /**
         * Check if the tech can support the given source
         *
         * @param srcObj The source object
         * @return {string} The options passed to the tech
         */
        OgvJS.canPlaySource = (srcObj) => {
                return OgvJS.canPlayType(srcObj.type);
        };

        /**
         * Check if the volume can be changed in this browser/device.
         * Volume cannot be changed in a lot of mobile devices.
         * Specifically, it can't be changed on iOS and iPadOS.
         *
         * @return {boolean} True if volume can be controlled.
         */
        OgvJS.canControlVolume = () => {
                if (getDeviceOS() === iPhoneOS || getDeviceOS() === iPadOS) {
                        return false;
                }
                const p = new OGVPlayer();

                return p.hasOwnProperty('volume');
        };

        /**
         * Check if the volume can be muted in this browser/device.
         *
         * @return {boolean} True if volume can be muted.
         */
        OgvJS.canMuteVolume = () => {
                return true;
        };

        /**
         * Check if the playback rate can be changed in this browser/device.
         *
         * @return {boolean} True if playback rate can be controlled.
         */
        OgvJS.canControlPlaybackRate = () => {
                return true;
        };

        /**
         * Check to see if native 'TextTracks' are supported by this browser/device.
         *
         * @return {boolean} True if native 'TextTracks' are supported.
         */
        OgvJS.supportsNativeTextTracks = () => {
                return false;
        };

        /**
         * Check if the fullscreen resize is supported by this browser/device.
         *
         * @return {boolean} True if the fullscreen resize is supported.
         */
        OgvJS.supportsFullscreenResize = () => {
                return true;
        };

        /**
         * Check if the progress events is supported by this browser/device.
         *
         * @return {boolean} True if the progress events is supported.
         */
        OgvJS.supportsProgressEvents = () => {
                return true;
        };

        /**
         * Check if the time update events is supported by this browser/device.
         *
         * @return {boolean} True if the time update events is supported.
         */
        OgvJS.supportsTimeupdateEvents = () => {
                return true;
        };

        /**
         * Boolean indicating whether the 'OgvJS' tech supports volume control.
         *
         * @type {boolean}
         * @default {@link OgvJS.canControlVolume}
         */
        /**
         * Boolean indicating whether the 'OgvJS' tech supports muting volume.
         *
         * @type {boolean}
         * @default {@link OgvJS.canMuteVolume}
         */
        /**
         * Boolean indicating whether the 'OgvJS' tech supports changing the speed at which the media plays.
         * Examples:
         *   - Set player to play 2x (twice) as fast.
         *   - Set player to play 0.5x (half) as fast.
         *
         * @type {boolean}
         * @default {@link OgvJS.canControlPlaybackRate}
         */
        /**
         * Boolean indicating whether the 'OgvJS' tech currently supports native 'TextTracks'.
         *
         * @type {boolean}
         * @default {@link OgvJS.supportsNativeTextTracks}
         */
        /**
         * Boolean indicating whether the 'OgvJS' tech currently supports fullscreen resize.
         *
         * @type {boolean}
         * @default {@link OgvJS.supportsFullscreenResize}
         */
        /**
         * Boolean indicating whether the 'OgvJS' tech currently supports progress events.
         *
         * @type {boolean}
         * @default {@link OgvJS.supportsProgressEvents}
         */
        /**
         * Boolean indicating whether the 'OgvJS' tech currently supports time update events.
         *
         * @type {boolean}
         * @default {@link OgvJS.supportsTimeupdateEvents}
         */
        [
                ['featuresVolumeControl', 'canControlVolume'],
                ['featuresMuteControl', 'canMuteVolume'],
                ['featuresPlaybackRate', 'canControlPlaybackRate'],
                ['featuresNativeTextTracks', 'supportsNativeTextTracks'],
                ['featuresFullscreenResize', 'supportsFullscreenResize'],
                ['featuresProgressEvents', 'supportsProgressEvents'],
                ['featuresTimeupdateEvents', 'supportsTimeupdateEvents']
        ].forEach(([key, fn]) => {
                defineLazyProperty(OgvJS.prototype, key, () => OgvJS[fn](), true);
        });

        Tech.registerTech('OgvJS', OgvJS);

        return OgvJS;

}));