Froogaloop Vimeo API -- Can't call API methods outside of Ready Event - javascript

I've scoured the docs in SO and Vimeo and can't seem to figure out how to call an API method outside of the Ready Event in Vimeo. I have created my Vimeo player, embedding it into teacher.js:
$(".video-player").prepend('<iframe id="player" src="//player.vimeo.com/video/'+video_id+'?api=1&player_id=player&badge=0&title=0&byline=0&portrait=0" width="100%" height="100%" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>');
player = $('iframe#player')[0];
vimeoSetup(player);
Which then calls vimeoSetup in a different script:
function vimeoSetup (player) {
$f(player).addEvent('ready', ready);
function addEvent(element, eventName, callback) {
if (element.addEventListener) {
element.addEventListener(eventName, callback, false);
}
else {
element.attachEvent('on'+eventName, callback);
}
}
function ready(player_id) {
// Keep a reference to Froogaloop for this player
container = document.getElementById(player_id).parentNode.parentNode;
froogaloop = $f(player_id);
var buttons = container.querySelector('div#video-player-controls-holder-teacher div'),
playBtn = document.querySelector('#play-pause');
//play-pause -- I wanted to have a single play-pause button, which is working fine
addEvent(playBtn, 'click', function() {
froogaloop.api('paused', function(paused) {
if(paused) {
froogaloop.api('play');
} else {
froogaloop.api('pause');
}
});
});
...
Now, if I want to say call $f(player).api('pause'); in teacher.js, I get this error: Unable to post message to http://player.vimeo.com. Recipient has origin mydomain. It seems like such a simple problem--I'm not sure if it involves usage of 'this' that is currently beyond me, or if I'm grabbing the Vimeo player embed incorrectly--I did get a lot of "no method .api for this object" in experimenting.
The end goal is that I can create a vimeo player, provide controls (both of these are good), and then use the API to call methods that feed into backbone, including pause, play, and time.
Are there other events, aside from 'click' and user-generated events I can use? Like an event that says another function was called? Seems circuitous...My backbone view looks like this:
pause: function () {
this.player.pauseVideo(); //this is for a YouTube API which works great
//I want to be able to similarly call something like froogaloop.api('pause');
},
Thanks so much--StackOverflow has taught me an amazing amount.

So, if 'ready' event is working, then everything must be setup right. But here is one possible problem.
In:
player = $('iframe#player')[0];
vimeoSetup(player);
You are getting an Object by id 'player' and then, in vimeoSetup(player); passing an Object into $f(player) which is working.
Yet, further down the code in ready function you are passing in to $f(player_id) just an id, not an Object, therefore api calls not working.
You just need to get Object again by player_id and pass it into $f(), or save player = $('iframe#player')[0]; as global var and then call all API methods on it (though can be not a good option, if You want to make something like few dinamicly spawning players each one with own controls, or if You just one of those, who scared of global variables)

Related

Embed.ly API: player.js not working with youku.com

I have a problem with embedded video. I use "Embed.ly API" and "player.js":
https://docs.embed.ly/docs/oembed
https://docs.embed.ly/docs/playerjs
I'm trying to embed a video from "youku.com" on my site. The "youku.com" is a content provider, as stated here: https://embed.ly/providers
Video is added but player's methods (play, pause and others) does not work for video. But if I changing video service from "youku.com" to "youtube.com" and use video from "youtube.com", then methods works well.
I also tried to connect the "platform.js" instead of the player. The result is the same.
Maybe the "player.js" worked before, I guess that the "youku.com" has changed its API, but the "player.js" did not track changes.
Does the player.js really work with "youku.com" now? Perhaps something should be added to my code for solve the problem? I am not in China, can it have any effect due to the fact that "youku.com" is a Chinese service?
Thanks.
My code js: (And another question: are there any errors in the code?)
var obj_json = $.getJSON('https://api.embedly.com/1/oembed?' + $.param({
url: :url, // for example: https://v.youku.com/v_show/id_XMzg2MjgwNzA0OA==.html
key: :key // my_API_key
})).done(function () {
$('.embeded').html(obj_json.responseJSON.html).find('iframe.embedly-embed').each(function () {
// initialize the player.
var player = new playerjs.Player(this);
player.on('ready', function () {
player.setLoop(true); // it is not work
player.play(); // it is not work
//..................................
// There are also special buttons on the page, clicking on which the video should start or stop:
$('.button_play').click(function () {
player.play(); // pressing occurs, but player it is not work
});
$('.button_pause').click(function () {
player.pause(); // pressing occurs, but player it is not work
});
//.........................................
});
});
});

How to listen volume changes of youtube iframe?

Here I found an example how I can listen Play\Pause button of youtube iframe.
player.addEventListener('onStateChange', function(e) {
console.log('State is:', e.data);
});
Now I need to listen the volume changes.
In the youtube documentation and here I found a method player.getVolume(), but I have no idea how this method can be implemented if I want to be informed about volume changes from iframe side, instead of ask iframe from my side.
On YouTube Player Demo page such functionality exists (when I change the volume of a player, I see appropriate changes in the row Volume, (0-100) [current level: **]), but neither in the doc nor in internet I can not find how to implement it.
I also tried to use the above mentioned code with onApiChange event (it is not clear for me what this event actually does), like:
player.addEventListener('onApiChange', function(e) {
console.log('onApiChange is:', e.data);
});
but console shows nothing new.
player.getOptions(); shows Promise {<resolved>: Array(0)}.
Could anyone show an example?
See this question.
You can listen to postMessage events emitted by the IFrame and react only to the volume change ones:
// Instantiate the Player.
function onYouTubeIframeAPIReady() {
var player = new YT.Player("player", {
height: "390",
width: "640",
videoId: "dQw4w9WgXcQ"
});
// This is the source "window" that will emit the events.
var iframeWindow = player.getIframe().contentWindow;
// Listen to events triggered by postMessage.
window.addEventListener("message", function(event) {
// Check that the event was sent from the YouTube IFrame.
if (event.source === iframeWindow) {
var data = JSON.parse(event.data);
// The "infoDelivery" event is used by YT to transmit any
// kind of information change in the player,
// such as the current time or a volume change.
if (
data.event === "infoDelivery" &&
data.info &&
data.info.volume
) {
console.log(data.info.volume); // there's also data.info.muted (a boolean)
}
}
});
}
See it live.
Note that this relies on a private API that may change at anytime without previous notice.
I inspected the code of YouTube Player Demo page and found that the html line which shows the current YouTube volume (<span id="volume">**</span>) constantly blinking (~ 2 times per 1 sec), so I can assume this demo page uses something like this:
// YouTube returns Promise, but we need actual data
self = this
setInterval(function () { self.player.getVolume().then(data => { self.volumeLv = data }) }, 250)
Possibly not the best method, but it seems there is no other option (I also tried to listen changes in the appropriate style of the volume bar, but no luck due to the cross-origin problem).
So, this let us 'listen' volume changes of youtube.
Just in case, if someone wants to set youtube volume, you need to use [this.]player.setVolume(volume_from_0_to_100)

Detect playback status of youtube video on any website

I want to write a web extension that will be able to detect playback status of any youtube video a user visits. I looked into the youtube API but it seems like I can only access youtube videos that I've embedded myself. However in this situation, I am not embedding them.
Is there any way to do this?
I've been intrigued by this question since it was asked, and tonight at our local GDG we had a Chrome extension hackathon where I put some experiments to the test. Here's what I've discovered.
A) the extension sandbox really is tough; nothing I tried could get at the javascript coded into the page (if it existed).
B) one solution that works pretty well under certain circumstances is to use a content-script to do code injection, and set up your own listeners on an existing iframe embed. Something like this works:
var regex = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w]*(?:['"][^<>]*>|<\/a>))[?=&+%\w]*/ig;
var node, nodes = document.evaluate("(//iframe/#src)",document,null,XPathResult.UNORDERED_NODE_ITERATOR_TYPE,null);
var hasvid=false,vids = [];
while (node=nodes.iterateNext()) { // uses regex to run through DOM, looking for embeds
if (regex.test(node.nodeValue)) {
hasvid=true;
vids.push(node.ownerElement);
}
}
// Now inject a script that sets up a YT.player object and bind it to our extension's functions
if (hasvid) {
playerlines=['function onYouTubePlayerAPIReady() {'];
for (i=0;i<vids.length; i++) {
playerlines.push('player = new YT.Player(\''+vids[i].id+'\', { events: {\'onStateChange\': onPlayerStateChange}});}');
}
callbacklines=['function onPlayerStateChange(event) {',
'if (event.data == YT.PlayerState.ENDED) { alert(\'video is over\'); }',
'else if (event.data == YT.PlayerState.PAUSED) { alert(\'player is paused\'); }',
'else if (event.data == YT.PlayerState.PLAYING) { alert(\'player is playing\'); } }'];
// notify the background page to actually do the injecting of the script
chrome.extension.sendRequest({}, function(response) {});
codelines=playerlines.concat(callbacklines);
var actualCode=codelines.join('\n');
var script = document.createElement('script');
script.textContent = actualCode;
var jsapi = document.createElement('script');
jsapi.src = "http://www.youtube.com/player_api";
(document.head||document.documentElement).appendChild(jsapi);
(document.head||document.documentElement).appendChild(script);
With something like that, you could not just run alerts, but theoretically send all events back to your background page so it could do what it wants based on what the events are.
C) This is of no use if the video wasn't embedded with an iFrame; I tried to employ a similar solution when it was an or element, and while I could detect it just fine, I couldn't get listeners set up to capture the events. I may have been overlooking something, or it may be that the events are fired differently so I couldn't get the binding I needed to.
D) One less-than-useful workaround would be to manipulate the DOM and remove an embedded video, re-embedding it yourself with more direct bindings and listeners set up (as another answer suggested). However, if the original page had been using the API itself, such an action would break its listeners, so there could be unintended consequences if the video you're seeing is part of a API-based web app.
Hope this is useful info.
Well, so embed the videos yourself!
You can use content-scripts to modify the youtube frame before it loads, or even better...
I'm not sure how youtube api works, but you can probably intercept video request with Chrome's Web Request API and alter the request data to get what you want.
If you provide more info, may I can give you more details on how do that...
Good luck!

YouTube API Target (multiple) existing iframe(s)

I'm trying to understand how to target an existing iframe using the YouTube API (i.e. without constructing an iframe with the script).
As usual, Google does not give enough API examples, but explains that it IS possible, here http://code.google.com/apis/youtube/iframe_api_reference.html
Here is an example of what I'm trying to do - the video underneath the thumbnail should play. I am almost there, but only the first video plays...
http://jsfiddle.net/SparrwHawk/KtbYR/2/
TL;DR: DEMO: http://jsfiddle.net/KtbYR/5/
YT_ready, getFrameID and onYouTubePlayerAPIReady are functions as defined in this answer. Both methods can be implemented without any preloaded library. In my previous answer, I showed a method to implement the feature for a single frame.
In this answer, I focus on multiple frames.
HTML example code (important tags and attributes are capitalized, <iframe src id>):
<div>
<img class='thumb' src='http://i2.cdnds.net/11/34/odd_alan_partridge_bio_cover.jpg'>
<IFRAME ID="frame1" SRC="http://www.youtube.com/embed/u1zgFlCw8Aw?enablejsapi=1" width="640" height="390" frameborder="0"></IFRAME>
</div>
<div>
<img class='thumb' src='http://i2.cdnds.net/11/34/odd_alan_partridge_bio_cover.jpg'>
<IFRAME ID="frame2" SRC="http://www.youtube.com/embed/u1zgFlCw8Aw?enablejsapi=1" width="640" height="390" frameborder="0"></IFRAME>
</div>
JavaScript code (YT_ready, getFrameID, onYouTubePlayerAPIReady and the YouTube Frame API script loader are defined here)
var players = {}; //Define a player storage object, to expose methods,
// without having to create a new class instance again.
YT_ready(function() {
$(".thumb + iframe[id]").each(function() {
var identifier = this.id;
var frameID = getFrameID(identifier);
if (frameID) { //If the frame exists
players[frameID] = new YT.Player(frameID, {
events: {
"onReady": createYTEvent(frameID, identifier)
}
});
}
});
});
// Returns a function to enable multiple events
function createYTEvent(frameID, identifier) {
return function (event) {
var player = players[frameID]; // Set player reference
var the_div = $('#'+identifier).parent();
the_div.children('.thumb').click(function() {
var $this = $(this);
$this.fadeOut().next().addClass('play');
if ($this.next().hasClass('play')) {
player.playVideo();
}
});
}
}
In my previous answer, I bound the onStateChange event. In this example, I used the onReady event, because you want to call the functions only once, at initialization.
This example works as follows:
The following methods are defined in this answer.
The YouTube Frame API is loaded from http://youtube.com/player_api.
When this external script has finished loading, onYoutubePlayerAPIReady is called, which in his turn activates all functions as defined using YT_ready
The declaration of the following methods are shown here, but implemented using this answer. Explanation based on the example:
Loops through each <iframe id> object, which is placed right after <.. class="thumb">.
At each frame element, the id is retrieved, and stored in the identifier variable.
The internal ID of the iframe is retrieved through getFrameID. This ensures that the <iframe> is properly formatted for the API. In this example code, the returned ID is equal to identifier, because I have already attached an ID to the <iframe>.
When the <iframe> exists, and a valid YouTube video, a new player instance is created, and the reference is stored in the players object, accessible by key frameID.
At the creation of the player instance, a **onReady* event is defined. This method will be invoked when the API is fully initialized for the frame.
createYTEvent
This method returns a dynamically created function, which adds functionality for separate players. The most relevant parts of the code are:
function createYTEvent(frameID, identifier) {
return function (event) {
var player = players[frameID]; // Set player reference
...
player.playVideo();
}
}
frameID is the ID of the frame, used to enable the YouTube Frame API.
identifier is the ID as defined in YT_ready, not necessarily an <iframe> element. getFrameID will attempt to find the closest matching frame for a given id. That is, it returns the ID of a given <iframe> element, or: If the given element is not an <iframe>, the function looks for a child which is a <iframe>, and returns the ID of this frame. If the frame does not exists, the function will postfix the given ID by -frame.
players[playerID]` refers to the initialized player instance.
Make sure that you also check this answer, because the core functionality of this answer is based on that.
Other YouTube Frame API answers. In these answers, I showed various implementations of the YouTube Frame/JavaScript API.

mediaElementjs: how to get instance of the player

I'm stuck with a little problem with MediaElement.js player.
To get the instance of the player, I do this (works with html5 compatible browser):
// Get player
this.playerId = $('div#shotlist-player video').attr('id');
this.player = window[this.playerId];
But it's not working as soon as it fallback in flash. In fact, it's not working because I'm not calling an instance of MediaElement itself. But I don't see how I can call it.
The player is created with
$('video').mediaelementplayer({....});
How can I get the mediaelement object?
------------EDIT----------------
Ok I finally found how to make it works:
// Get player
mePlayer = $('div#shotlist-player video.video-js')[0];
this.player = new MediaElementPlayer(mePlayer);
Now I can user mediaElement instance correctly.
This post is a lot of speculation, but may be correct. Docs are lacking (;
The answer by sidonaldson is perfectly acceptable if you wish to create a new MediaElement instance and get a handle on it. If there's one already present, it seems to try to reinitialize another instance on that element and freaks out.
I am pretty sure mediaelement.js augments the builtin HTML5 controls by providing a JavaScript API to manipulate Flash/Silverlight players via those elements. I may be wrong, but other advice I've seen on this issue in multiple places is to do something like:
$playButton.click(function() {
$('video, audio').each(function() {
$(this)[0].player.play();
});
});
To create a play button as an external DOM element which will fire off all players on the page. This indicates to me that the implementation is something like I've described.
Try:
var player = $('video').mediaelementplayer({
success: function (me) {
me.play();
}
});
// then you can use player.id to return the id
// or player.play();

Categories