I have the following code snippet that controls an embedded youtube player. It works great on Chrome and Safari but not on Firefox.
jsfiddle : http://jsfiddle.net/fuSSn/4/
Code from my app:
the iframe:
<div class="tubeframe" id="video-frame-155" style="">
<iframe title="YouTube video player" width="350" height="262" src="http://www.youtube.com/embed/hc5xkf9JqoE?HD=1;rel=0;showinfo=0;autohide=1" frameborder="0" allowfullscreen="" id="video-frame-155-frame"></iframe>
</div>
my javascript:
var source_tag = document.createElement("script");
var first_source_tag = document.getElementsByTagName("script")[0];
first_source_tag.parentNode.insertBefore(source_tag, first_source_tag);
// This function will be called when the API is fully loaded
function onYouTubeIframeAPIReady() {
YT_ready(true)
console.log("api loaded! yikes")
}
function getFrameID(id){
var elem = document.getElementById(id);
if (elem) {
if(/^iframe$/i.test(elem.tagName)) return id; //Frame, OK
// else: Look for frame
var elems = elem.getElementsByTagName("iframe");
if (!elems.length) return null; //No iframe found, FAILURE
for (var i=0; i<elems.length; i++) {
if (/^https?:\/\/(?:www\.)?youtube(?:-nocookie)?\.com(\/|$)/i.test(elems[i].src)) break;
}
elem = elems[i]; //The only, or the best iFrame
if (elem.id) return elem.id; //Existing ID, return it
// else: Create a new ID
do { //Keep postfixing `-frame` until the ID is unique
id += "-frame";
} while (document.getElementById(id));
elem.id = id;
return id;
}
// If no element, return null.
return null;
}
// Define YT_ready function.
var YT_ready = (function(){
var onReady_funcs = [], api_isReady = false;
return function(func, b_before){
if (func === true) {
api_isReady = true;
while(onReady_funcs.length > 0){
// Removes the first func from the array, and execute func
onReady_funcs.shift()();
}
}
else if(typeof func == "function") {
if (api_isReady) func();
else onReady_funcs[b_before?"unshift":"push"](func);
}
}
})();
var video = function ( videoid, frameid) {
var player;
var that;
var seconds;
var duration;
var stateChangeCallback;
var update_play = 0;
return {
setOnStateChangeCallback: function(callback) {
stateChangeCallback = callback;
},
getCurrentTime: function() {
return player.getCurrentTime();
},
getPlayer: function () {
return player;
},
getVideoFrameId: function () {
return "video-frame-" + videoid;
},
initVideo: function (second) {
console.log("initing")
that = this;
YT_ready(function(){
var frameID = getFrameID("video-frame-" + videoid);
console.log("creating player")
console.log(frameID)
if (frameID) { //If the frame exists
console.log("frame exists")
player = new YT.Player(frameID, {
events: {
"onStateChange": that.stateChange
}
});
console.log("Player Created!");
if (second) {
console.log(second)
setTimeout(function() { console.log("seek to"); player.seekTo(second, false); player.stopVideo()}, 1000);
}
}
});
},
stateChange: function (event) {
console.log("event.data = ", event.data);
switch(event.data) {
case YT.PlayerState.PLAYING:
{
if (stateChangeCallback)
stateChangeCallback("play", player.getCurrentTime(), player.getDuration());
onsole.log("play");
}
break;
case YT.PlayerState.PAUSED:
case YT.PlayerState.CUED:
case YT.PlayerState.ENDED:
{
if (stateChangeCallback)
stateChangeCallback("pause", player.getCurrentTime(), player.getDuration());
console.log("pause");
}
break;
}
},
pauseVideo: function () {
player.stopVideo();
console.log('player.stopVid()');
},
seekTo: function(second) {
player.seekTo(second, false);
}
};
};
function onStateChange(vid, action, second, total) {
if (Videos[vid]) {
console.log( (second / total) * 100);
}
};
$(document).ready(function () {
var Videos = {};
logger.info("heyyy")
var videoId=155;
//if (videoId) {
Videos[videoId] = video(videoId, 155);
console.log(Videos[155])
Videos[155].initVideo();
Videos[155].setOnStateChangeCallback(function(action, second, total) {
onStateChange(155, action, second, total);
});
//}
Videos[155].seekTo(1000, false);
onStateChange(155, "start", 0, 0);
});
I know that the required script tags are being added, I can test that from console. I also know that onYouTubePlayerAPIReady() is actually called. But I still receive errors like
TypeError: player.stopVideo is not a function
When I run the three lines that adds the source tag again from the web console on firefox, the api seems to load and everything starts working again.
I have been struggling with this for days and I really need help figuring out what might be wrong. If it helps my application is developed in ruby on rails but I don't think this is relevant information.
Thanks
There is no problem with the above code. My video was loaded in a bootstrap modal. Modal's hide property would make it invisible to firefox and firefox would not load the api at all. So I removed the modal hide class and instead of display:none I used item.css("visibility", "visible"); and item.css("visibility", "hidden"); which made firefox load the api.
Related
I have a site with a banner video that worked fine when we were running jquery2. After upgrading to jquery 3, the banner video no longer works. Can anyone look at the code and see what is wrong? When I look at the console in chrome, I see this error (as well as a cookie error) : jquery.min.js:2 Uncaught TypeError: e.indexOf is not a function
at S.fn.init.S.fn.load (jquery.min.js:2)
at (index):532
<video id="BannerVideo" class="Banner" muted playsinline loop oncanplaythrough="playBannerVideo()"></video>
<script>
function playBannerVideo()
{
var videoElement = document.getElementById('BannerVideo');
try
{
videoElement.play();
}
catch(err) {}
}
var basePosterURL = '/FCC/media/Images/Home2018/Banner/Banner.jpg';
var baseVideoURL = '/FCC/media/Images/Home2018/Banner/BannerVideo.mp4';
function getNameForCurrentWidth()
{
var bodyWidth = document.body.clientWidth; // $('body').width();
if (bodyWidth <= 767)
return '_Phone';
if (bodyWidth <= 990)
return '_Tablet';
return ''; // Desktop
}
var loadedWidthName;
function loadBannerVideo(posterOnly)
{
var newWidthName = getNameForCurrentWidth();
if(newWidthName == loadedWidthName)
return;
loadedWidthName = newWidthName;
var videoElement = document.getElementById('BannerVideo');
if (!videoElement.paused)
videoElement.pause();
videoElement.src = '';
videoElement.poster = addToFilename(basePosterURL, loadedWidthName);
if (posterOnly == true)
return;
setBannerVideoSrc(loadedWidthName);
}
function setBannerVideoSrc(newWidthName)
{
// don't play video if in design or edit modes
if( $('body.DesignMode').length == 1 || $('body.EditMode').length == 1)
return;
var videoElement = document.getElementById('BannerVideo');
videoElement.src = addToFilename(baseVideoURL, newWidthName);
videoElement.load();
}
// load poster as soon as possible
$(function() // document.ready
{
loadBannerVideo(true);
});
// load and play video after everything else loads
$(window).load(function()
{
setBannerVideoSrc(loadedWidthName);
});
var resizeTimeout;
$(window).resize(function()
{
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(resizeEnd, 200);
});
function resizeEnd()
{
loadBannerVideo();
}
</script>
According to this article with the same error, the load(), error() and toggle() event listeners are deprecated. Meaning you're using out of date code that isn't supported by jQuery no more.
Analyzing your code I see that you use the load() event listener. The fix here would be to use the on to listen to the load event.
$(window).on('load', function() {
// ...
});
But your code uses so little jQuery and way more vanilla JavaScript that it makes little sense to use jQuery at all. I'd recommend that you ditch it if you don't use it anywhere else. In case you do, the snippet below is the equivalent of what you posted, but without jQuery.
function playBannerVideo() {
var videoElement = document.getElementById("BannerVideo");
try {
videoElement.play();
} catch (err) {}
}
var basePosterURL = "/FCC/media/Images/Home2018/Banner/Banner.jpg";
var baseVideoURL = "/FCC/media/Images/Home2018/Banner/BannerVideo.mp4";
function getNameForCurrentWidth() {
var bodyWidth = document.body.clientWidth;
if (bodyWidth <= 767) return "_Phone";
if (bodyWidth <= 990) return "_Tablet";
return ""; // Desktop
}
var loadedWidthName;
function loadBannerVideo(posterOnly) {
var newWidthName = getNameForCurrentWidth();
if (newWidthName == loadedWidthName) return;
loadedWidthName = newWidthName;
var videoElement = document.getElementById("BannerVideo");
if (!videoElement.paused) videoElement.pause();
videoElement.src = "";
videoElement.poster = addToFilename(basePosterURL, loadedWidthName);
if (posterOnly == true) return;
setBannerVideoSrc(loadedWidthName);
}
function setBannerVideoSrc(newWidthName) {
// don't play video if in design or edit modes
if (document.querySelector("body.DesignMode").length == 1 || document.querySelector("body.EditMode").length == 1)
return;
var videoElement = document.getElementById("BannerVideo");
videoElement.src = addToFilename(baseVideoURL, newWidthName);
videoElement.load();
}
// load poster as soon as possible
loadBannerVideo(true);
// load and play video after everything else loads
window.addEventListener('load', function() {
setBannerVideoSrc(loadedWidthName);
});
var resizeTimeout;
window.addEventListener('resize', function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(resizeEnd, 200);
});
function resizeEnd() {
loadBannerVideo();
}
I'm using a module to detect when the user is speaking, called hark. Here's some of the code:
// original source code is taken from:
// https://github.com/SimpleWebRTC/hark
// copyright goes to &yet team
// edited by Muaz Khan for RTCMultiConnection.js
function hark(stream, options) {
var audioContextType = window.webkitAudioContext || window.AudioContext;
var harker = this;
harker.events = {};
harker.on = function (event, callback) {
harker.events[event] = callback;
};
harker.emit = function () {
if (harker.events[arguments[0]]) {
harker.events[arguments[0]](arguments[1], arguments[2], arguments[3], arguments[4]);
}
};
// make it not break in non-supported browsers
if (!audioContextType) return harker;
options = options || {};
// Config
var smoothing = (options.smoothing || 0.1),
interval = (options.interval || 50),
threshold = options.threshold,
play = options.play,
history = options.history || 10,
running = true;
(...)
return harker;
}
What is this line for?
var harker = this;
When I checked in the debugger, this stores a Window object in harker. And from what I'm seeing it makes for some unexpected behavior when I call hark more than once.
Why not just do var harker;?
Full code is here:
https://www.webrtc-experiment.com/hark.js
And here's a demo where it's used:
<style>
html, body {
margin: 0!important;
padding: 0!important;
}
video {
width: auto;
max-width: 100%;
}
</style>
<title>Auto Stop RecordRTC on Silence</title>
<h1>Auto Stop RecordRTC on Silence</h1>
<br>
<button id="btn-start-recording">Start Recording</button>
<button id="btn-stop-recording" disabled style="display: none;">Stop Recording</button>
<hr>
<video controls autoplay playsinline></video>
<script src="/RecordRTC.js"></script>
<script src="https://www.webrtc-experiment.com/hark.js"></script>
<script>
var video = document.querySelector('video');
var h1 = document.querySelector('h1');
var default_h1 = h1.innerHTML;
function captureCamera(callback) {
navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(camera) {
callback(camera);
}).catch(function(error) {
alert('Unable to capture your camera. Please check console logs.');
console.error(error);
});
}
function stopRecordingCallback() {
video.srcObject = null;
var blob = recorder.getBlob();
video.src = URL.createObjectURL(blob);
recorder.camera.stop();
video.muted = false;
}
var recorder; // globally accessible
document.getElementById('btn-start-recording').onclick = function() {
this.disabled = true;
captureCamera(function(camera) {
video.muted = true;
video.srcObject = camera;
recorder = RecordRTC(camera, {
type: 'video'
});
recorder.startRecording();
var max_seconds = 3;
var stopped_speaking_timeout;
var speechEvents = hark(camera, {});
speechEvents.on('speaking', function() {
if(recorder.getBlob()) return;
clearTimeout(stopped_speaking_timeout);
if(recorder.getState() === 'paused') {
// recorder.resumeRecording();
}
h1.innerHTML = default_h1;
});
speechEvents.on('stopped_speaking', function() {
if(recorder.getBlob()) return;
// recorder.pauseRecording();
stopped_speaking_timeout = setTimeout(function() {
document.getElementById('btn-stop-recording').click();
h1.innerHTML = 'Recording is now stopped.';
}, max_seconds * 1000);
// just for logging purpose (you ca remove below code)
var seconds = max_seconds;
(function looper() {
h1.innerHTML = 'Recording is going to be stopped in ' + seconds + ' seconds.';
seconds--;
if(seconds <= 0) {
h1.innerHTML = default_h1;
return;
}
setTimeout(looper, 1000);
})();
});
// release camera on stopRecording
recorder.camera = camera;
document.getElementById('btn-stop-recording').disabled = false;
});
};
document.getElementById('btn-stop-recording').onclick = function() {
this.disabled = true;
recorder.stopRecording(stopRecordingCallback);
};
</script>
<footer style="margin-top: 20px;"><small id="send-message"></small></footer>
<script src="https://www.webrtc-experiment.com/common.js"></script>
The pattern of assigning the value of this to a variable is something you can read more about by searching for this that pattern (or self = this ) for example, since that's a common name for the variable for "saving" a reference to this.
The reason for doing that is that this changes depending on the context of functions. If you assign this at a specific scope you can pass that along to other functions - since they wouldn't be able to use this, since this might mean something completely different to them.
I want to know when the audio is still playing , and when it stopped to perform other actions . None of these options works for me. I tried for reproduce a same file, but only if is stopped, I try for a few days, but it still not work
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, function () {
$scope.media2.play();
}, function (b) {
}, function (a) {
alert(JSON.stringify(a));
});
}
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, bien,mal,status);
function bien(){ $scope.media2.play();}
function mal(){}
function status(estatus){ console.log("status")}
}
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, bien,mal,status);
function bien(){ $scope.media2.play();}
function mal(){}
function status(estatus){ alert(status)}
}
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, bien,mal,getStatusMessage);
function bien(){ $scope.media2.play();}
function mal(){}
function getStatusMessage(status){
if(status === 0){console.log('Media.MEDIA_NONE');}
else if(status === 1){console.log('Media.MEDIA_STARTING');}
else if(status === 2){console.log('Media.MEDIA_RUNNING');}
else if(status === 3){console.log('Media.MEDIA_PAUSED');}
else if(status === 4){console.log('Media.MEDIA_STOPPED');}
}
}
I have done this by creating a blank media player, then later setting the media. To test if it is playing just perform a check that tests if your media player is null or undefined.
$scope.media;
$scope.media = new Media("some/media/path.mp3);
if ($scope.media != null) {
someAction();
}
else {
otherAction();
}
Another option is to test if the media length is greater than 0.
$scope.media.getCurrentPosition(
//success
function (position) {
if (position > 0) {
console.log("A song is playing!");
}
},
// error
function () {
console.log("An error occured!");
}
);
finally it worked . I put this code and i realize a downgrade to cordova-media-plugin to 1.0.1 version
function reproducirAudioPorBoton(ruta) {
var audiofile = new Media('file:///android_asset/www/sounds/M.mp3');
audiofile.play();
var counter=0;
var timerDur = setInterval(function() {
counter = counter + 100;
if (counter > 2000) {
clearInterval(timerDur);
}
var dur = audiofile.getDuration();
if (dur > 0) {
clearInterval(timerDur);
console.log(dur + " sec");
}
}, 100);
}
I want to play the songs only if the user press the button 'play',
without Waiting to load all the 21 songs.
when I press the button 'play' the page jump up like refreshing ,it is not normal.
what I can do to improve my code.
please try to play the songs in the example site and see the problem.
many thanks.
var Music = {
init:function(){
song = new Audio();
//speaKer = document.getElementById("imgSpeaker");
this.volume = 0.08;
this.isMute = false;
canPlayMP3 = (typeof song.canPlayType === "function" && song.canPlayType("audio/mpeg") !== "");
song.src = canPlayMP3 ? "http://rafih.co.il/wp-content/uploads/2013/07/1.mp3" : "http://rafih.co.il/wp-content/uploads/2013/07/1.ogg";
song.preload = 'auto';
},
/* start:function(){
song.src = canPlayMP3 ? "http://rafih.co.il/wp-content/uploads/2013/07/1.mp3" : "http://rafih.co.il/wp-content/uploads/2013/07/1.ogg";
song.volume = 0.08;
song.autoplay = true;
song.load();
},*/
play: function () {
song.volume = 0.08;
song.autoplay = true;
song.load();
// Music.speaker();
},
stop: function () {
if (!song.ended) {
song.pause();
}
},
next: function () {
if (curr < count) {
curr++;
}else curr = 1;
song.src = canPlayMP3 ? "http://rafih.co.il/wp-content/uploads/2013/07/" + curr + ".mp3" : "http://rafih.co.il/wp-content/uploads/2013/07/" + curr + ".ogg";
},
};
function load() {
Music.init();
}
You need to return false from the play buttons event handler, to prevent the page from scrolling to the top.
Change:
<div id="play" onclick='Music.play()'>
To:
<div id="play" onclick='Music.play(); return false;'>
That's because of the anchor in your link, such as href="#". A very simple fix would be to add return false; in to your inline handler attribute, such as onclick="Music.play(); return false;", however that isin't a good approach and is considered a bad practice.
Instead you could add your handlers programmatically, it will also make your code more modular and testable:
var Music = {
init: function (options) {
var playBtn = this.playBtn = document.querySelector(options.playBtn);
playBtn.addEventListener('click', this._onPlayBtnClick.bind(this));
//...
return this;
},
_onPlayBtnClick: function (e) {
e.preventDefault(); //prevents the browser's default behaviour
this.play();
},
//...
};
Then you could do something like:
function load() {
var musicController = Object.create(Music).init({
playBtn: '#play',
//...
});
}
You could also use it as a singleton like:
Music.init({ ... });
I'm trying to implement a site with a slider that plays videos. I followed one of the answers here to be able to stop the sliders from drifting off while a video was playing, but now I need to be able to stop the actual playback of the video when the user leaves the slide.
This is my current code:
// Define YT_ready function.
var YT_ready = (function(){
var onReady_funcs = [], api_isReady = false;
/* #param func function Function to execute on ready
* #param func Boolean If true, all qeued functions are executed
* #param b_before Boolean If true, the func will added to the first
position in the queue*/
return function(func, b_before){
if (func === true) {
api_isReady = true;
for (var i=0; i<onReady_funcs.length; i++){
// Removes the first func from the array, and execute func
onReady_funcs.shift()();
}
}
else if(typeof func == "function") {
if (api_isReady) func();
else onReady_funcs[b_before?"unshift":"push"](func);
}
}
})();
// This function will be called when the API is fully loaded
function onYouTubePlayerAPIReady() {YT_ready(true)}
// Load YouTube Frame API
(function(){ //Closure, to not leak to the scope
var s = document.createElement("script");
s.src = "http://www.youtube.com/player_api"; /* Load Player API*/
var before = document.getElementsByTagName("script")[0];
before.parentNode.insertBefore(s, before);
})();
var players = {};
//Define a player storage object, to enable later function calls,
// without having to create a new class instance again.
YT_ready(function() {
(function($) {
$(".framevideo").each(function(index) {
var identifier = this.id;
var frameID = getFrameID(identifier);
if (frameID) { //If the frame exists
players[frameID] = new YT.Player(frameID, {
events: {
"onStateChange": function(event) {
if(event.data == 1 || event.data == 3) {
//console.log("The video two is playing and the cycle is paused = " + event.data);
$('.flexslider').flexslider('pause');
}
else if(/* event.data == -1 || */ event.data == 0 || event.data == 2 || event.data == 5) {
//console.log("The video two is not playing and the cycle is started = " + event.data);
$('.flexslider').flexslider('play');
}
}
}
});
}
});
$('.flexslider').bind('before', function() {
for (var key in players)
{
/* this works in Chrome and IE9, doesn't work on Firefox?! */
players[key].pauseVideo();
}
});
})(jQuery);
});
I know that players is looping correctly, I've debugged it with console.log and I get the keys for all the players, but players[key].pauseVideo(); gives me an error TypeError: players[key].pauseVideo is not a function.
Thanks for any help.
I ended up writing the function so that it would load the YouTube player only when each slide was active, then storing it in the container object. It was the only way to ensure that it was displaying and didn't fail.
var players = {};
//Define a player storage object, to enable later function calls,
// without having to create a new class instance again.
YT_ready(function() {
(function($) {
createPlayers();
//console.log('binding');
$('.flexslider').bind('after', function() {
createPlayers();
});
$('.flexslider').bind('before', function() {
for (key in players) {
//console.log('pausing '+key);
players[key].pauseVideo();
}
});
})(jQuery);
});
// this function will check for all frames that don't have a display:none attribute, and create a player for them
function createPlayers() {
(function($) {
//console.log('attempting to create players');
$(".framevideo").each(function(index) {
var frameID = getFrameID(this.id);
if (frameID) { //If the frame exists
// we check if frame already has associated key in container object
if (!(frameID in players)) {
// now we check if the parent slider "row" is displayed
if ($(this).parents('.flexslider-views-slideshow-main-frame-row').css('display') !== 'none') {
// we create the player and add it to the container
//console.log('creating '+frameID);
players[frameID] = new YT.Player(frameID, {
events: {
"onStateChange": function(event) {
if(event.data == 1 || event.data == 3) {
$('.flexslider').flexslider('pause');
}
else if(/* event.data == -1 || */ event.data == 0 || event.data == 2 || event.data == 5) {
$('.flexslider').flexslider('play');
}
}
}
});
}
}
}
});
})(jQuery);
}