I'm looking into the vimeo embed api. I want the video to autoplay on load which I have working but currently what happens is like this:
player loads then autoplays [event "ready"]
thumbnail removed and shows black for about a second
video begins playing [event "playProgess"]
The problem is the second step. I'm trying to eliminate that black screen between when the thumbnail hides (when play is initiated) to when the video actually appears and starts playing.
The way I figure it can be solved is to keep the thumbnail around and trigger the thumbnail hide on the first "playProgress" but I can't seem to find anyway to control when the thumbnail turns on or off.
Is this possible to control? I'm aware that I can pull the thumbnail and overlay it over the iframe but I'm hoping for a cleaner fix (keep it all contained to the iframe).
Here's a pen with api running:
http://codepen.io/mattcoady/pen/KMzZMZ
$(function() {
var player = $('iframe');
var playerOrigin = '*';
var status = $('.status');
// Listen for messages from the player
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
}
else {
window.attachEvent('onmessage', onMessageReceived, false);
}
// Handle messages received from the player
function onMessageReceived(event) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
return false;
}
if (playerOrigin === '*') {
playerOrigin = event.origin;
}
var data = JSON.parse(event.data);
console.log(data.event);
switch (data.event) {
case 'ready':
onReady();
break;
case 'playProgress':
onPlayProgress(data.data);
break;
case 'pause':
onPause();
break;
case 'finish':
onFinish();
break;
case 'play':
onPlay();
break;
}
}
// Call the API when a button is pressed
$('button').on('click', function() {
post($(this).text().toLowerCase());
});
// Helper function for sending a message to the player
function post(action, value) {
var data = {
method: action
};
if (value) {
data.value = value;
}
var message = JSON.stringify(data);
player[0].contentWindow.postMessage(message, playerOrigin);
}
function onReady() {
status.text('ready');
post('play');
post('addEventListener', 'pause');
post('addEventListener', 'finish');
post('addEventListener', 'playProgress');
}
function onPause() {
status.text('paused');
}
function onFinish() {
status.text('finished');
}
function onPlay(){
alert('play')
}
function onPlayProgress(data) {
status.text(data.seconds + 's played');
}
});
What I ended up going with my hacky fix. It's pulls the thumbnail and lays it over the video. When my script detects the 'playProgress' event that means the video is actually playing. I use jQuery to fade away the thumbnail cover.
http://codepen.io/mattcoady/pen/YWqaWJ
$(function() {
var player = $('iframe');
var playerOrigin = '*';
var videoId = 76979871;
player.attr('src', 'https://player.vimeo.com/video/' + videoId + '?api=1&player_id=player1&background=1&autoplay=1&loop=1');
// Listen for messages from the player
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
} else {
window.attachEvent('onmessage', onMessageReceived, false);
}
$.getJSON('http://vimeo.com/api/v2/video/' + videoId + '.json', {jsonp: 'callback',dataType: 'jsonp'}, function(data) {
var thumbnail = document.createElement('img');
thumbnail.src = data[0].thumbnail_large;
thumbnail.style.width = document.querySelector('#player1').offsetWidth + 'px';
thumbnail.style.height = document.querySelector('#player1').offsetHeight + 'px';
document.querySelector('#vimeo-thumb-container').appendChild(thumbnail);
})
// Handle messages received from the player
function onMessageReceived(event) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {return false;}
if (playerOrigin === '*') { playerOrigin = event.origin; }
var data = JSON.parse(event.data);
switch (data.event) {
case 'ready':
onReady();
break;
case 'playProgress':
onPlayProgress(data.data);
break;
}
}
// Helper function for sending a message to the player
function post(action, value) {
var data = { method: action };
if (value) {data.value = value;}
var message = JSON.stringify(data);
player[0].contentWindow.postMessage(message, playerOrigin);
}
function onReady() {
post('play');
post('addEventListener', 'playProgress');
}
function onPlayProgress(data) {
$('#vimeo-thumb-container').fadeOut(250);
}
});
Related
I would like to play through Vimeo videos in sequence with JavaScript with continuous playback and looping through items. The following code moves to the second video at the end of the first but the same functions are not called at the end of the second video. Only the end of the first video shows Called! in the Javascript console, not the end of the second.
The Vimeo Player SDK has player.getVideoId() but not player.setVideoId() so I used a hack to load the next video: setting the source of the iframe and starting the Vimeo player anew. I wonder if this is the problem and the listener for the end of the video is attached to the discarded player.
Another issue with this code is that it exits fullscreen to load the next video.
I attempted this CodePen that sets the Vimeo Player on any element instead of an iframe and was unable to change the played video.
What is a cleaner or effective way of changing videos with the Vimeo Player SDK?
The HTML is:
<div class="vimeo">
<div class="resp-iframe-container container-centered">
<iframe class="resp-iframe" scrolling="no" frameborder="no" src="https://player.vimeo.com/video/238301525" allowfullscreen="" data-ready="true"></iframe></div></div>
And the javascript is:
var l = ["238301525", "496371201", "238301525", "496371201", "..."];
window.current = 0;
var increment = function() {
window.current += 1;
if (window.current == l.length) {
window.current = 0;
}
};
var decrement = function() {
window.current -= 1;
if (window.current < 0) {
window.current = l.length - 1;
}
};
prevA.addEventListener("click", function() {
decrement();
loadPlayer();
});
nextA.addEventListener("click", function() {
increment();
console.log("here!");
loadPlayer();
console.log("there!");
});
var loadPlayer = function() {
console.log("Called!");
var iframe = document.querySelector('iframe');
iframe.src = "https://player.vimeo.com/video/" + l[window.current];
var player = new Vimeo.Player(iframe);
player.autoplay = true;
var treatEvent = function(data, eventLabel) {
// Custom code...
if ("end" == eventLabel) {
console.log("incrementing automatically");
increment();
loadPlayer();
}
};
var onPlay = function(data) {
treatEvent(data, "play");
}
var onPause = function(data) {
treatEvent(data, "pause");
}
var onSeeked = function(data) {
treatEvent(data, "seeked");
}
var onEnd = function(data) {
treatEvent(data, "end");
}
player.on('play', onPlay);
player.on('pause', onPause);
player.on('seeked', onSeeked);
player.on('ended', onEnd);
setTimeout(function() {
//console.log("Trying to play...");
player.play().then(function() {
// the video was played
console.log("success: video played");
}).catch(function(error) {
console.log("Error! " + error.name);
});
}, 1000);
}
loadPlayer();
It seems that the Vimeo Player keeps track of whether it was initialized, adding a tag data-vimeo-initialized="true" in the HTML element.
One solution is to use the Vimeo Player SDK function .loadVideo():
var song_iframe = document.querySelector("iframe#song_video");
var song_player = new Vimeo.Player(song_iframe);
var on_song_end = function(data) {
// Logic to get the new id.
song_player.loadVideo(newId).then(function() {
song_player.play();
});
};
song_player.on("ended", on_song_end);
I'm trying to make a video downloader for instagram but whenever I fetch the video url it does not download I have to right click and save. is there a way I could make a download button instead of right click to save?
here my code
$("#getMedia").click(getMedia);
var render = document.querySelector("#media");
function createMedia(data, type) {
var media = document.createElement(type);
media.id = "instagramMedia";
media.src = data.content;
media.setAttribute("class", "");
media.width = $(render).width();
if (type === "video") {
media.controls = true;
media.autoplay = true;
}
render.innerHTML = "";
var downloadMsg = document.createElement("p");
downloadMsg.setAttribute("class", "bg-success text-info");
downloadMsg.innerText = "Right Click on the Media below to get Save Option!";
render.appendChild(downloadMsg);
render.appendChild(media);
}
function getMedia() {
var url = $("#postUrl").val();
if (url) {
$.get(url, function (data) {
render.innerHTML = data;
var mediaWaitTimer = setTimeout(function () {
var video = document.querySelector('meta[property="og:video"]');
if (video) {
createMedia(video, "video");
} else {
var img = document.querySelector('meta[property="og:image"]');
if (img) {
createMedia(img, "img");
} else {
document.body.innerHTML = body;
alert("Error extracting Instagram image / video.");
};
}
clearTimeout(mediaWaitTimer);
}, 200);
});
} else {
document.querySelector("#media").setAttribute("placeholder", "Invalid Address, Please Enter Proper Insagram Link");
}
}
Set download attribute for your clickable html element probably in your createMedia function.
<a href="myvideo.mp4" download>Download</a>
for reference visit : https://www.w3schools.com/tags/att_a_download.asp
I have a page in which clicking a link opens a lightbox and embeds a YouTube video in an <iframe>. The lightbox and <iframe> markup are generate on the fly by Lity.
Following the example right out of the documentation, where the <iframe> is hard-coded into the page, it works as expected.
<iframe id="existing-iframe-example"
width="640" height="360"
src="https://www.youtube.com/embed/M7lc1UVf-VE?enablejsapi=1"
frameborder="0"
style="border: solid 4px #37474F">
</iframe>
<script type="text/javascript">
var tag = document.createElement('script');
tag.id = 'iframe-demo';
tag.src = 'https://www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubeIframeAPIReady() {
console.log('api ready');
player = new YT.Player('existing-iframe-example', {
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
document.getElementById('existing-iframe-example').style.borderColor = '#FF6D00';
}
function changeBorderColor(playerStatus) {
var color;
if (playerStatus == -1) {
color = "#37474F"; // unstarted = gray
} else if (playerStatus == 0) {
color = "#FFFF00"; // ended = yellow
} else if (playerStatus == 1) {
color = "#33691E"; // playing = green
} else if (playerStatus == 2) {
color = "#DD2C00"; // paused = red
} else if (playerStatus == 3) {
color = "#AA00FF"; // buffering = purple
} else if (playerStatus == 5) {
color = "#FF6DOO"; // video cued = orange
}
if (color) {
document.getElementById('existing-iframe-example').style.borderColor = color;
}
}
function onPlayerStateChange(event) {
changeBorderColor(event.data);
}
</script>
See this Codepen
I'm trying to modify it to work with a dynamically generated <iframe> (which is how Lity handles YouTube videos). I can see that the YouTube API script is being added to the page, but the function onYouTubeIframeAPIReady does not ever seem to be called, which according to documentation is supposed to fire as soon as API script loads.
HTML (to open lightbox)
<a href="https://www.youtube.com/embed/M7lc1UVf-VE?enablejsapi=1" class="lity" data-lity>open</a>
JavaScript (fires after lightbox object is available in DOM)
$(document).on("lity:ready", function (event, instance) {
console.log("Lightbox ready");
$(".lity-youtube iframe").attr("id", "x");
if ($("iframe#x").length) {
console.log("ID added to <iframe>");
} else {
console.log("ID NOT added to <iframe>");
}
var tag = document.createElement("script");
tag.id = "iframe-demo";
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
if ($("#iframe-demo").length) {
console.log("api script added");
} else {
console.log("api script NOT added");
}
var player;
function onYouTubeIframeAPIReady() {
console.log("api ready");
player = new YT.Player("x", {
events: {
onReady: onPlayerReady,
onStateChange: onPlayerStateChange
}
});
}
function onPlayerReady(event) {
console.log("player ready");
document.getElementById("x").style.borderColor = "#FF6D00";
}
function changeBorderColor(playerStatus) {
var color;
if (playerStatus == -1) {
color = "#37474F"; // unstarted = gray
} else if (playerStatus == 0) {
color = "#FFFF00"; // ended = yellow
} else if (playerStatus == 1) {
color = "#33691E"; // playing = green
} else if (playerStatus == 2) {
color = "#DD2C00"; // paused = red
} else if (playerStatus == 3) {
color = "#AA00FF"; // buffering = purple
} else if (playerStatus == 5) {
color = "#FF6DOO"; // video cued = orange
}
if (color) {
document.getElementById("x").style.borderColor = color;
}
}
function onPlayerStateChange(event) {
console.log('state change');
changeBorderColor(event.data);
}
});
See this Codepen.
I tried pulling everything out of the lity:ready event handler with the same results....
Can anyone see what's going wrong?
EDIT/UPDATE
I tried running onYouTubeIframeAPIReady as a deferred object
var YTdeferred = $.Deferred();
window.onYouTubeIframeAPIReady = function() {
YTdeferred.resolve(window.YT);
};
and using it in the callback
var player;
YTdeferred.done(function (YT) {
console.log("api ready");
player = new YT.Player("x", {
events: {
//onReady: onPlayerReady,
//onStateChange: onPlayerStateChange
onReady: function () {
console.log("player ready from ananonymous");
},
onStateChange: onPlayerStateChange
}
});
console.log(player);
});
Now the YT.Player object is created (presumably on the iframe created by lity), but neither of the events (onReady and onStateChange) seem to be triggering. Also, it doesn't have access to any of the methods or properties that it should:
player.playVideo()
produces an error: player.playVideo is not a function`
Still stumped.
Codepen
ANOTHER EDIT/UPDATE
Now I'm very confused because i created a version that replicates what lity does (I think) and the events (onReady and onStateChange) do in fact fire as expected.
Codepen
Solution to a STUPID mistake
I can' believe I beat my head into the wall over this for so long... G'AAAAAAH!!
It's definitely a lity thing
When lity receives an argument (YouTube URL) formatted like
https://www.youtube.com/embed/XXXXXXXXX
or
http://youtu.be/XXXXXXXXX
it converts it to
https://www.youtube.com/embed/XXXXXXXXX?autoplay=1
If you try to pass query string arguments to to a similarly formatted URL (such as: https://www.youtube.com/embed/XXXXXXXXX?enablejsapi=1 (which is required for the API calls to work), it URLencodes the ?enablejsapi=1 and adds it to the end of the converted URL, so you get:
https://www.youtube.com/embed/XXXXXXXX?autoplay=1&%3Fenablejsapi=1
As a result the enablejsapi=1 is never read in and therefore the YT.Player is never attached to the <iframe> DOM element.
** SOLUTION **
Pass the URL to lity in a format that passes the video ID as a query string argument (and append enablejsapi=1):
https://www.youtube.com/watch?v=XXXXXXXX&enablejsapi=1
lity will now keep your argument(s) intact and convert to
https://www.youtube.com/embed/XXXXXXXX?autoplay=1&enablejsapi=1
Now the YT.Player call can read enablejsapi=1 and properly attach to the <iframe> element, and all is good in the universe.
<a href="https://www.youtube.com/watch?v=TJU-tBquzcM&enablejsapi=1" class="lity" data-lity>open lightbox with video</a>
Final Working CodePen
.
It looks like the "lity:ready" event is not fired in my case.
I changed
$(document).on("lity:ready", function (event, instance) {
to
$(document).ready(function (event, instance) {
using the jQuery ready event.
And most important, the function onYouTubeIframeAPIReady must be global (on window scope) to be detected by the youtube iFrame API (actually it was created inside an anonymous function() ). So I changed
function onYouTubeIframeAPIReady() {
to
window.onYouTubeIframeAPIReady=function() {
Codepen
EDIT:
To work on a global context inside an anonymous function. Other local functions created and referenced (onPlayerReady and onPlayerStateChange) must be declared in the global context using the window object.
window.onPlayerReady=function(event) {
console.log("player ready");
document.getElementById("x").style.borderColor = "#FF6D00";
}
window.changeBorderColor=function(playerStatus) {
var color;
if (playerStatus == -1) {
color = "#37474F"; // unstarted = gray
} else if (playerStatus == 0) {
color = "#FFFF00"; // ended = yellow
} else if (playerStatus == 1) {
color = "#33691E"; // playing = green
} else if (playerStatus == 2) {
color = "#DD2C00"; // paused = red
} else if (playerStatus == 3) {
color = "#AA00FF"; // buffering = purple
} else if (playerStatus == 5) {
color = "#FF6DOO"; // video cued = orange
}
if (color) {
document.getElementById("x").style.borderColor = color;
}
}
window.onPlayerStateChange=function(event) {
console.log('state change');
changeBorderColor(event.data);
}
I'm working on an online radio website and am trying to detect what state the audio player is currently in for the user.
The current setup I've got is often wrong in knowing this and if I'm on iOS and I pause from the lock screen player instead, it will still think it's playing because I never clicked the html pause button.
Finally how is it possible to detect when my stream has fully ended. I've tried: onstalled="", onwaiting="", onerror="", onended="". But none of them work 100% of the time. The closest one to work would be: onstalled="", but even that only had a 60% success rate or so (occasionally when I'm loading the site it would tell me it has ended).
HTML:
<audio autoplay="" id="player" title="" oncanplay="radioLoaded()">
<source src="...">
</audio>
Javascript:
function radioLoaded() {
if (player.paused) {
document.getElementById('radioplaypause').innerHTML = varRadioResume;
} else if (player.play) {
document.getElementById('radioplaypause').innerHTML = varRadioPause;
} else {
document.getElementById('radioplaypause').innerHTML = varRadioLoading;
}
window.player = document.getElementById('player');
document.getElementById('radioplaypause').onclick = function () {
if (player.paused) {
player.play();
this.innerHTML = varRadioPause;
} else {
player.pause();
this.innerHTML = varRadioResume;
}
}
};
function radioEnded() {
document.getElementById('radiolivetext').innerHTML = 'OFFLINE';
document.getElementById('radioplayer').style.display = 'none';
document.getElementById('radioinformation').style.display = 'none';
};
Try this, with some corrections/notes to the code in general:
function radioLoaded() {
// move this line to the top
window.player = document.getElementById('player'); // !!! <-- name too generic, possibility of global collisions
var playPauseButton = document.getElementById('radioplaypause');
if (player.paused) {
playPauseButton.innerHTML = varRadioResume;
} else if (player.play) {
playPauseButton.innerHTML = varRadioPause;
} else {
playPauseButton.innerHTML = varRadioLoading;
}
playPauseButton.onclick = function () {
if (player.paused) {
player.play();
this.innerHTML = varRadioPause;
} else {
player.pause();
this.innerHTML = varRadioResume;
}
};
player.onended = function () {
console.log('ended');
};
// other helpful events
player.onpause = function () {
console.log('paused...');
}
player.onplay = function () {
console.log('playing...');
}
player.onprogress = function (e) {
console.log(e.loaded + ' of ' + e.total + ' loaded');
}
};
I've added onkeydown event listener to document, and when the event fires, I play an audio file. This audio file is a gun sound. If you tap the key once, it plays fine, but if you press and hold, the audio repeats incredibly fast. I've solved that with a simple playing condition check, but I have another problem. If I press and hold, only after the first shot, it sounds like the gun is firing repeatedly. For example, it goes like ta-tatatatata.
How can I fix my machine gun? Make it fire like tatatata.
Demo
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function playAudio(file) {
if (!playing) {
var audio = new Audio(file);
audio.play();
playing = true;
setTimeout(function() {
playing = false;
}, 100);
}
}
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
playAudio(weapons.aug.audio);
break;
}
}
Hey let me know if it works I hope it helps !
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function loadAudio(file) {
var audio = new Audio(file);
return audio;
}
function playAudio(file) {
audio.play();
}
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
var gunsound = loadAudio(weapons.aug.audio);
playAudio(gunsound);
setTimeout(function() {},200);
break;
}
}
There should probably be a better way with a gameLoop and less globals, but wait for the ended event of the audio object to tag playing at false and be sure not to start playing your sound unless it's finished.
var playing = false;//tag playing as false by default
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function playAudio(file) {
if (!playing) {
audio = new Audio(file);
playing = true;
setTimeout(function(){
playing = false;
},400);
audio.play();
}
}
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
playAudio(weapons.aug.audio);
break;
}
}
akinuri, this was my first answer, but I deleted it (I thought you wanted to play the whole sound), so I undeleted it, it could help you (it will fix the delay bug of keydown).
You should use a gameLoop (common pattern in game development), with requestAnimationFrame.
Don't trigger events directly from player's input, set states that will be processed inside the gameLoop (which should be in charge of updating positions, rendering ...)
var playing = false;
var keyStatePressed = false;//set state at false at init
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function playAudio(file) {
if (!playing) {
var audio = new Audio(file);
audio.play();
playing = true;
setTimeout(function() {
playing = false;
}, 100);
}
}
//flag at true on keydown
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
keyStatePressed = true;
break;
}
}
//run a game loop
function gameLoop(){
if(keyStatePressed === true){
playAudio(weapons.aug.audio);
keyStatePressed = false;//reset state
}
//run this in 16ms (see more on game loops and requestAnimationFrame)
requestAnimationFrame(gameLoop);
}
//launch the gameLoop
gameLoop();
I started to code this before thinking about it, and that was my first mistake. I've solved the problem now. All I had to do was simply mimic the FPS games' fire command. Start an interval with the mousedown and clear the interval with mouseup.
Instead of using mouse click, I had to use key press. So when I press a key (and hold), an interval starts and plays the audio repeatedly. When I release the key, interval is cleared, audio stops.
Demo
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A",
interval : null,
firstShot : false,
}
};
function playAudio(file, weapon) {
if (!weapon.interval) {
if (!weapon.firstShot) {
var audio = new Audio(file);
audio.play();
weapons.aug.firstShot = true;
}
weapon.interval = setInterval(function() {
var audio = new Audio(file);
audio.play();
}, 150);
}
}
document.onkeydown = function(e) {
switch (e.keyCode) {
case 65:
playAudio(weapons.aug.audio, weapons.aug);
break;
}
}
document.onkeyup = function(e) {
switch (e.keyCode) {
case 65:
clearInterval(weapons.aug.interval);
weapons.aug.interval = null;
weapons.aug.firstShot = false;
break;
}
}