Code Monkey home page Code Monkey logo

ember-youtube's Introduction

ember-youtube

An Ember.js component to play and control YouTube videos using the iframe API. Pass it a YouTube video ID and you're good to go! Every day this component is being used on Radio4000.

You can see a demonstration at ember-youtube.surge.sh.

Features

  • Full support for all YouTube player events (and errors)
  • Custom (external) controls (make your own buttons)
  • Custom progress bar in full sync with the YouTube player
  • Extra: custom time properties (for instance "4:31 / 7:58") formatted with Moment.js

TravisCI Build Status

Quick start

Inside your Ember CLI project run:

ember install ember-youtube

Use the component like this:

{{ember-youtube ytid="fZ7MhTRmJ60"}}

Here's another example with all options. Only ytid is required.

{{ember-youtube
	ytid="fZ7MhTRmJ60"
	volume=100
	playerVars=customPlayerVars
	showDebug=false
	showControls=false
	showProgress=false
	lazyload=false
	delegate=this
	delegate-as="emberYoutube"
	playing=(action "ytPlaying")
	paused=(action "ytPaused")
	ended=(action "ytEnded")
	buffering=(action "ytBuffering")
}}

YouTube player options

The YouTube API allows you to define an object of options called playerVars. With ember-youtube, you can optionally set this object on the component:

// controller.js
myPlayerVars: {
  autoplay: 1,
  showinfo: 0,
  // Setting an origin can remove a YouTube 'postMessage' API warning in the console.
  // Note, this does not have any effect on localhost.
  origin: 'https://www.example.com'
}
{{ember-youtube ytid="fZ7MhTRmJ60" playerVars=myPlayerVars}}

External controls

If you want your own buttons to control the player there are two steps.

  1. Make the ember-youtube component available to the outside, which normally means your controller. You do this with the delegate and delegate-as properties of ember-youtube. They expose the component and give you a target for your button's actions. Like this:
{{ember-youtube ytid=youTubeId delegate=controller delegate-as="emberYoutube"}}
  1. Specify a target on your actions. Now, and because we used delegate and delegate-as, you'll have a emberYoutube property on your controller. This is where we'll target our actions. It allows you to do this in the template where you include the player:
{{ember-youtube ytid="fZ7MhTRmJ60" delegate=this delegate-as="emberYoutube"}}
<button {{action "togglePlay" target=emberYoutube}}>
	{{#if emberYoutube.isPlaying}}Pause{{else}}Play{{/if}}
</button>
<button {{action "toggleVolume" target="emberYoutube"}}>
	{{#if emberYoutube.isMuted}}Unmute{{else}}Mute{{/if}}
</button>

You could also do this:

{{ember-youtube ytid="fZ7MhTRmJ60" delegate=this delegate-as="emberYoutube"}}
<button {{action "play" target=emberYoutube}}>Play</button>
<button {{action "pause" target=emberYoutube}}>Pause</button>
<button {{action "mute" target=emberYoutube}}>Mute</button>
<button {{action "unMute" target=emberYoutube}}>Unmute</button>

Seeking

Here's an example of seeking to a certain timestamp in a video. It accepts a number of seconds.

<button {{action "seekTo" 90 target=emberYoutube}}>Seek to 01:30</button>
{{ember-youtube ytid="fZ7MhTRmJ60" delegate=this delegate-as="emberYoutube"}}

Events

The ember-youtube component send four different actions: playing, paused, ended and buffering. You should map them to your own actions like this:

{{ember-youtube ytid="fZ7MhTRmJ60"
	playing="ytPlaying"
	paused="ytPaused"
	ended="ytEnded"
	buffering="ytBuffering"}}
actions: {
  ytPlaying(event) {},
  ytPaused(event) {},
  ytEnded(event) {
    // here you could load another video by changing the youTubeId
  },
  ytBuffering(event) {}
}

Lazy load

Even if you don't supply an ytid to the ember-youtube component, it will make sure the iframe player is created as soon as possible. But if you set lazyload=true, it will wait for an ytid. This will, in some cases, improve the initial render performance. Example:

{{ember-youtube lazyload=true}}

Custom timestamps

Let's write a component with two custom formatted timestamps such as "13:37". First make sure moment and moment-duration-format are installed. Then create a new component with the following template:

{{ember-youtube ytid=youTubeId delegate=this delegate-as="emberYoutube"}}

// custom timestamp
<p class="EmberYoutube-time">
	{{currentTimeFormatted}}/{{durationFormatted}}
</p>

And here's the JavaScript part of the component:

export default Ember.Component.extend({
	currentTimeFormat: 'mm:ss',
	durationFormat: 'mm:ss',

	// returns a momentJS formated date based on "currentTimeFormat" property
	currentTimeFormatted: computed('emberYoutube.currentTime', 'currentTimeFormat', function () {
		let time = this.get('emberYoutube.currentTime');
		let format = this.get('currentTimeFormat');
		if (!time || !format) {
			return null;
		}
		let duration = moment.duration(time, 'seconds');
		return duration.format(format);
	}),

	// returns a momentJS formated date based on "durationFormat" property
	durationFormatted: computed('emberYoutube.duration', 'durationFormat', function () {
		let duration = this.get('emberYoutube.duration');
		let format = this.get('durationFormat');
		if (!duration || !format) {
			return null;
		}
		let time = moment.duration(duration, 'seconds');
		return time.format(format);
	})
});

Autoplay on iOS

On iOS autoplay of videos is disabled by Apple to save your precious data. I haven't been able to circumvent this. The user needs to tap the video itself before we can call the player's play/load methods. If anyone has a workaround, let me know.

Development

Linting

  • npm run lint:js
  • npm run lint:js -- --fix

Running tests

  • ember test – Runs the test suite on the current Ember version
  • ember test --server – Runs the test suite in "watch mode"
  • npm test – Runs ember try:each to test your addon against multiple Ember versions

Please file an issue if you have any feedback or would like to contribute.

Thanks to https://github.com/oskarrough/ember-youtube/graphs/contributors.

This project is licensed under the MIT License.

ember-youtube's People

Contributors

0xadada avatar anulman avatar bummzack avatar cigoe avatar denisnazarov avatar dependabot[bot] avatar donb1991 avatar ember-tomster avatar gnazarkin avatar jkatsnelson avatar kidgodzilla avatar lancedikson avatar lordkada avatar mcfiredrill avatar naomak avatar oskarrough avatar plasmapower avatar williamweckl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ember-youtube's Issues

playerVars

Hi, do you plan on making the playerVars settable? So it is possible to hide YT controls for example.

Edit:
I just successfully passed a playerVars object to the component declaration like so:

// in parent component
playerVars: {
  autoplay: 0,
  controls: 0,
  enablejsapi: 1,
  rel: 0, // disable related videos
  showinfo: 0,
  autohide: 1,
  fs: 0, // disable fullscreen button
  playsinline: 1,
  // disablekb: 1,
  // iv_load_policy: 3,
  modestbranding: 1
}
// in template
{{ember-youtube ytid="oIzNGxOFIKI"  playerVars=playerVars}}

Which works just fine, but something like this would of course be nicer:

{{ember-youtube ytId="oIzNGxOFIKI"  ytControls=false}}

😍

Autoplay issues in iOS

The ember-youtube component auto-plays videos once it receives a new ytid, but on iOS this causes the player to break.

According to Apple's documentation,

"...embedded media cannot be played automatically in Safari on iOS - the user always initiates playback."

Instead of solely using loadVideoById, we should be using cueVideoById in cases like this. As a workaround I used a simple viewport size check, but there should probably be a cleaner way to resolve this problem. My workaround:

loadVideo: function() {
    var id = this.get('ytid'),
        load = (window.innerWidth ||document.body.clientWidth) > 600 ? 'loadVideoById':'cueVideoById';
    if (!id) return;
    this.get('player')[load](id);
}.observes('ytid'),

P.S. great project! I was able to extend it pretty easily. Will try to open a PR one of these days.

Add support for changing isMuted and isPlaying.

Currently, changing isMuted and isPlaying have no effect. This means there is no good way to mute or pause the player.

Edit: For future visitors, it would appear I am supposed to use delegate-as to do this.

Raises an exception if the component is destroyed before it initializes.

Because of the async nature of the initialization it's possible for someone to have removed the component that's being initialized raising:

Couldn't find the iframe element to create a YouTube player

This is very evident on mobile browsers for us where we have the player in a modal that can be closed and removed. The cause is here: https://github.com/oskarrough/ember-youtube/blob/master/addon/components/ember-youtube.js#L133

This in turn ends up getting logged as an error since the exception is raised all the way to the global exception logger, but it isn't really since the user has intentionally removed the element. I think there should be a check to see if the component has been destroyed or not.

Switching between multiple videos triggers a widget api error

I had experienced this within a code base I am currently working on and noticed it in the demo example here, to recreate in the demo:

  1. Play a video on the main page.
  2. Select the link at the bottom that states 'heres another video'
  3. The above triggers the following Message in the console:
    www-widgetapi.js:100 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.youtube.com') does not match the recipient window's origin ('http://ember-youtube.surge.sh').

Unsure as to what causes this I have seen other frameworks with the same problem and multiple solutions proposed for each but nothing definitive.

Can't get ember-youtube to work on iOS safari or chrome

Not sure what is going on here, but I'm hoping I could get a pointer.

On Chrome and Safari iOS when I go to Zetaohm.com, I don't see my youtube videos load at the bottom. In Safari on OS X, and Chrome on OS X it works fine.

In Safari iOS I get the following error:

Unable to post message to https://www.youtube.com. Recipient has origin http://zetaohm.herokuapp.com.

on both OS X and iOS, I get the following error:

Blocked a frame with origin "https://www.youtube.com" from accessing a frame with origin "http://zetaohm.com". The frame requesting access has a protocol of "https", the frame being accessed has a protocol of "http". Protocols must match.
but on OS X it works fine.

Any Ideas?

ember-concurrency should be a dependency

This package doesn't list ember-concurrency as a dependency, but it should, since the youtube component it exposes directly consumes it.

from the addon docs:

Addons can also use other addons as dependencies. For example, this partial package.json is from the addon ember-power-select. It uses the addons ember-concurrency, ember-text-measurer, and ember-truth-helpers to support its functionality.

In this case, Ember CLI will incorporate these addons along with the other dependencies when ember-power-select is used in your applications.

So, if the consuming application uses the youtube component from ember-youtube, and that component imports ember-concurrency, it is a direct dependency and should be included as a dependency not a devDependency.

This is a problem in the case where the consuming application has tests or another addon with a dummy app consumes ember-youtbe.

This can be seen with the following type of error from my app:

not ok 202 Chrome 74.0 - [undefined ms] - Global error: Uncaught InvalidStateError: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'json'). at http://localhost:7357/31425136537553/tests/index.html?hidepassed, line 96
 After execution of test: ember-qunit: Ember.onerror validation: Ember.onerror is functioning properly
    ---
        Log: |
            { type: 'warn',
              text:
               '\'Error occurred while evaluating `mir/components/ember-youtube`: Could not find module `ember-concurrency` imported from `ember-youtube/components/ember-youtube`\\nError: Could not find module `ember-concurrency` imported from `ember-youtube/components/ember-youtube`\\n    at missingModule (http://localhost:7357/assets/vendor.js:252:11)\\n    at findModule (http://localhost:7357/assets/vendor.js:263:7)\\n    at Module.findDeps (http://localhost:7357/assets/vendor.js:173:24)\\n    at findModule (http://localhost:7357/assets/vendor.js:267:11)\\n    at Module.findDeps (http://localhost:7357/assets/vendor.js:173:24)\\n    at findModule (http://localhost:7357/assets/vendor.js:267:11)\\n    at requireModule (http://localhost:7357/assets/vendor.js:29:15)\\n    at http://localhost:7357/31425136537553/tests/index.html?hidepassed:49:11\\n    at Array.forEach (<anonymous>)\\n    at sendCoverage (http://localhost:7357/31425136537553/tests/index.html?hidepassed:44:33)\'\n' }

The fix is to move these external dependencies to devDependencies.

I've created a new ember app that adds this addon, and reproduces the bug:
https://github.com/0xadada/reproduction-ember-youtube-example

Uncaught Error: Could not find module `ember-concurrency` imported from `ember-youtube/components/ember-youtube`
    at missingModule (loader.js:247)
    at findModule (loader.js:258)
    at Module.findDeps (loader.js:168)
    at findModule (loader.js:262)
    at Module.findDeps (loader.js:168)
    at findModule (loader.js:262)
    at requireModule (loader.js:24)
    at Class._extractDefaultExport (index.js:422)
    at Class.resolveOther (index.js:103)
    at Class.resolve (index.js:163)

Different instances of component have mutual playerVars

Hello! I try to get number of players with different settings on a page, but run into the bug.

{{#ember-youtube ytid=lastVideo.ytId width=840 height=474}}1{{/ember-youtube}}
{{#ember-youtube ytid=lastVideo.ytId width=840 height=474 autoplay=true}}2{{/ember-youtube}}
{{#ember-youtube ytid=lastVideo.ytId width=840 height=474 autoplay=true}}3{{/ember-youtube}}

In this case we have 3 players and the first of them without autoplay. But it run every time with others because of by some reason it has the same playerVars (that applied by one of two latest instances).

sendAction deprication

Hi.

Can .sendAction() API calls be replaced by method calls, so there are no deprication warnings?

Update NPM version of ember-youtube

Hi there,

There have been few updates/merges in the past few months but the NPM version of ember-youtube doesn't reflect these desirable changes. It would be great if you can update npm with the latest version of ember-youtube. 😄

Ping me if I can be of help!

Ziyad

modernize addon

This addon could use some modernization luvin 💖:

  • convert sendAction to closure actions #50
  • remove dependency on jQuery #55
  • convert to use latest Ember LTS (3.8)
  • use prettier to standardize and enforce code style #56
  • convert addon to allow consumption via angle bracket syntax
  • convert addon to use native ES classes

Progress bar won't update on click if video is paused

Clicking on the <progress> element won't update currentTime unless (until) video is playing. When video is paused, clicking on the HTML progress bar will only update the Youtube bar, resulting in a disconnection between the two. Pressing play will sync them back together again.

Full screen?

Hi,

is it possible to enable the full screen on mobile?

It doesn't work when I switch routes

Hi, I run into problem with YT API initialisation: when I got a component on the page it works fine for the first time rendering. But if I change route, and return back to the page with the component, and it doesn't work this time. This is because YT api frame doesn't load www-widget.js if it is loaded already. I'm going to send PR with fix that.

Automatically stop playback when compontent is removed. Play video when component is added.

First of all, let me thank you for creating this component. It's really useful!

There's an issue when a user navigates away from a route with a video, when the video is still playing. In that case, the setTimeout will still continue to fire and create lots of JS errors.

There should be a willDestroyElement handler that stops/unloads the video (or at least stop the timeout from running).

Similarly, there's an issue when a user revisits a route with a video, where the video won't show at all. Example: There's a video and something route. User starts at / and then visits video. The video playback starts. Then the user navigates to something and later back to video, resulting in no video-playback.

In this case, there should probably be a handler for didInsertElement that makes sure the video will be initialized?

What do you think? Or is there an obvious thing I'm missing here and this isn't an issue at all?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.