Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ember 4 / Glimmer support #13

Open
nova-alex opened this issue May 17, 2023 · 1 comment
Open

Ember 4 / Glimmer support #13

nova-alex opened this issue May 17, 2023 · 1 comment

Comments

@nova-alex
Copy link

nova-alex commented May 17, 2023

Hi! I've been playing around with this addon and it's rather nice.
Unfortunately it's a classic component and although I did manage to get it working on its own, I wasn't able to import into and Ember 4 app.

I'm not great with creating addons and all that stuff but I do have the js and hbs files here which I'd like to offer to anyone who might like to use them.

videojs-player.hbs

<video class="video-js" {{did-insert this.didInsertHandler}} {{did-update this.didUpdateHandler}} {{will-destroy this.willDestroyHandloer}} autoplay={{this.autoplay}} controls={{this.controls}} loop={{this.loop}} muted={{this.muted}} playsinline={{this.playsinline}} preload="auto" poster={{this.poster}} data-setup="{}" crossorigin={{this.crossorigin}} tabindex="0">
  {{#if this.src}}
    <source src={{this.src}} type={{this.type}}>
  {{/if}}

  {{#if this.sources}}
    {{#each this.sources as |item|}}
      <source src={{item.src}} type={{item.type}}>
    {{/each}}
  {{/if}}

  {{#if this.textTrack}}
    <track kind="captions" src={{this.textTrack}} srclang="en" label="English" type="text/vtt" default>
  {{/if}}

  {{#if this.textTracks}}
    {{#each this.textTracks as |textTrack|}}
      <track kind={{textTrack.kind}} src={{textTrack.src}} srclang={{textTrack.language}} label={{textTrack.label}} type={{textTrack.type}} default>
    {{/each}}
  {{/if}}

  <p class="vjs-no-js">
    To view this video please enable JavaScript, and consider upgrading to a web browser that
    <a href="https://videojs.com/html5-video-support/" target="_blank" rel="noopener noreferrer">supports HTML5 video</a>
  </p>
</video>

videojs-player.js

import Component from '@glimmer/component';
import videojs from 'video.js';
// import { addObserver, removeObserver } from '@ember/object/observers';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

// https://github.com/IvyApp/ivy-videojs/blob/0bb2e1513bec874f9ce9cf48ffd3f6996623553b/addon/components/ivy-videojs.js
export default class VideojsPlayer extends Component {
  @tracked player = null;

  _defaults = {
    autoplay: false,
    controls: true,
    fluid: false,
    loop: false,
    muted: true,
    height: 520,
    width: 824,
    html5: true,
  };

  // _defaults
  get autoplay() {
    return this.args.autoplay || this._defaults.autoplay;
  }
  get controls() {
    return this.args.controls || this._defaults.controls;
  }
  get fluid() {
    return this.args.fluid || this._defaults.fluid;
  }
  get loop() {
    return this.args.loop || this._defaults.loop;
  }
  get muted() {
    return this.args.muted || this._defaults.muted;
  }
  get height() {
    return this.args.height || this._defaults.height;
  }
  get width() {
    return this.args.width || this._defaults.width;
  }
  get html5() {
    return this.args.html5 || this._defaults.html5;
  }

  get src() {
    return this.args.src;
  }

  get sources() {
    return this.args.sources;
  }

  get textTrack() {
    return this.args.textTrack;
  }

  get textTracks() {
    return this.args.textTracks;
  }

  get ready() {
    return this.args.ready;
  }

  playerEvents = null;

  /**
   * The set of video.js player events (and associated actions) to be set up
   * inside `didInsertElement`. If you need to respond to custom video.js
   * player events, such as those emitted by a plugin, you should do so by
   * calling `sendActionOnPlayerEvent` inside your `ready` handler.
   *
   * ```javascript
   * import Ember from 'ember';
   *
   * export default Ember.Controller.extend({
   *   actions: {
   *     ready(player, component) {
   *       component.sendActionOnPlayerEvent(player, 'actionName', 'eventName');
   *     }
   *   }
   * });
   * ```
   *
   * The above would send the `actionName` action in response to an `eventName`
   * event from the player.
   *
   * @property playerEvents
   * @type Object
   * @private
   */
  constructor(...args) {
    super(...args);

    this.playerEvents = [
      'abort',
      'canplay',
      'canplaythrough',
      'durationchange',
      'emptied',
      'ended',
      'error',
      'loadeddata',
      'loadedmetadata',
      'loadstart',
      'pause',
      'play',
      'playing',
      'progress',
      'ratechange',
      'resize',
      'seeked',
      'seeking',
      'stalled',
      'suspend',
      'timeupdate',
      'useractive',
      'userinactive',
      'volumechange',
      'waiting',
      'click',
      'tap',
    ];
  }

  @action
  didInsertHandler(element) {
    const playerOptions = {};
    playerOptions.html5 = this.html5 || {};

    if (this.liveui) {
      playerOptions.liveui = true;
    }

    let player = videojs(element, playerOptions);

    if (this.height) {
      player.height(this.height);
    }

    if (this.width) {
      player.width(this.width);
    }

    if (this.fluid) {
      player.fluid(this.fluid);
    }

    if (this.aspectRatio) {
      player.aspectRatio(this.aspectRatio);
    }

    // // Register plugins
    // // Get global plugins from config.
    // if (this['vr-projection']) {
    //   if (typeof player.vr === 'function') {
    //     this.set('crossorigin', 'anonymous');
    //     player.vr({ projection: this['vr-projection'] });
    //   } else {
    //     throw new EmberError(
    //       "It looks like you are trying to play a VR video without the videojs-vr library. Please `npm install --save-dev videojs-vr` and add `app.import('node_modules/videojs-vr/dist/videojs-vr.min.js');` to your ember-cli-build.js file."
    //     );
    //   }
    // }

    // https://github.com/IvyApp/ivy-videojs/blob/master/addon/components/ivy-videojs-player.js
    player.ready(() => {
      // Set up event listeners defined in `playerEvents`.
      let playerEvents = this.playerEvents;
      if (playerEvents) {
        playerEvents.forEach((val) => {
          this.sendActionOnPlayerEvent(player, val);
        });
      }

      // Let the outside world know that we're ready.
      if (this.ready && typeof this.ready === 'function') {
        this.ready(player, this);
      }
    });

    this.player = player;
  }

  @action
  didUpdateHandler(element) {
    let player = videojs(element);
    player.pause();
    player.src(this.src);
    player.load();
  }

  @action
  willDestroyHandler() {
    this.player.dispose();
  }

  /**
   * Sets up a listener that sends an action on a video.js player event.
   *
   * @method sendActionOnPlayerEvent
   * @param {Player} player the video.js player instance
   * @param {String} action the action name to be sent
   * @param {String} playerEvent the player event name to listen for
   */
  sendActionOnPlayerEvent(player, action) {
    const listenerFunction = (...args) => {
      console.log('Hey Hey', action);
      if (this.args[action]) {
        this.args[action](player, this, ...args);
      }
    };

    this._onPlayerEvent(player, action, listenerFunction);
  }

  /**
   * Sets the value of a property on a video.js player. If the property is
   * already equal to the given value, no change is made.
   *
   * @method setPlayerProperty
   * @param {Player} player the video.js player instance
   * @param {String} playerProperty the name of the property to set
   * @param {Object} value the value to set
   */
  setPlayerProperty(player, playerProperty, value) {
    const propertyMethod = player[playerProperty];
    if (propertyMethod) {
      const previousValue = propertyMethod.call(player);
      if (previousValue !== value) {
        propertyMethod.call(player, value);
      }
    }
  }

  // _addPlayerObserver(property, target, observer) {
  //   if (this.isDestroying) {
  //     return;
  //   }
  //   addObserver(this, property, target, observer);

  //   this.one('willDestroyElement', this, function () {
  //     removeObserver(this, property, target, observer);
  //   });
  // }

  _onPlayerEvent(player, eventName, listenerFunction) {
    player.on(eventName, listenerFunction);
  }
}

It relies on https://github.com/emberjs/ember-render-modifiers and can be used with @ properties

<VideojsPlayer @src="https://vjs.zencdn.net/v/oceans.mp4" @type="video/mp4"
	@poster="https://vjs.zencdn.net/v/oceans.png" @autoplay="true"
	@timeupdate={{this.timeupdate}} @ready={{this.ready}}/>

I hope this information is helpful. Currently I'm using it as an in-repo addon and I've commented out the observer portion because it can probably be done differently (i.e. using render helpers outside of this component) and the VR feature just cuz I'm not using it.

@nova-alex
Copy link
Author

This removes the jQuery dependency and simplifies the action handling a bit as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant