JavaScript audio player - javascript

I'm building a simple JavaScript audiotrack controller and I don't know what is wrong with my code. I've been searching for hours here and I found a simple code that did the job. I had to change a lot of things (due to typos or code errors) and I finally got this:
var TRACKS = [
{
id: 0,
nom: 'Johnny B. Goode',
autor: 'Chuck Berry',
src: 'src/johnnybgoode.mp3',
any: '1955'
},
{
id: 1,
nom: 'For Your Love',
autor: 'The Yardbirds',
src: 'src/foryourlove.mp3',
any: '1965'
}
];
var songs = 2;
var Player = function () {
"use strict";
var currentPlaying,
trackListPos,
trackList,
source,
audio;
this.getName = function () {
return currentPlaying.nom;
};
this.setTrack = function (obj) {
currentPlaying = obj;
source.src = obj.src;
audio.load();
audio.play();
return this;
};
this.setTrackList = function (t) {
trackList = t;
trackListPos = 0;
audio = document.getElementById('myAudio');
source = document.getElementById('audioSource');
this.setTrack(trackList[trackListPos]);
return this;
};
this.play = function () {
audio.load();
audio.play();
return this;
};
this.stop = function () {
audio.pause();
audio.currentTime = 0;
return this;
};
this.pause = function () {
audio.pause();
};
this.next = function () {
if (currentPlaying.id === (songs - 1)) {
trackListPos = 0;
} else {
trackListPos += 1;
}
this.setTrack(trackList[trackListPos]);
};
};
//Initialize
var reprod = new Player();
reprod.setTrackList(TRACKS).play();
function play() {
"use strict";
reprod.play();
document.getElementById("Play").innerHTML = "Pause";
}
function seguent() {
"use strict";
reprod.next();
document.getElementById("titol").innerHTML = "<b>Títol:</b>";
}
<audio id="myAudio" controls="controls">
<source id="audioSource" src="">
Your browser does not support the audio format.
</audio>
<nav class="navegador" id="sidebar">
<div class="playerwrapper">
<div class="player">
<p id="titol"></p>
<p id="autor"></p>
<p id="any"></p>
<button type="button" onclick="seguent()">Següent</button>
<button id="Play" type="button" onclick="play()">Play</button>
</div>
</div>
</nav>
As you can see there's a couple buttons that trigger the function play() and seguent() (which means next). And they don't seem to work.
The innerHTML that changes the Play button text doesn't work, but when I remove the "reprod.play();" line it does work and changes the button content.
Can someone explain what is exactly happening with my code?
(Sorry if the post is tedious, it's my second post here and I don't know the formatting)
Thanks for letting me know I could use a console, the errors that throws are:
Uncaught TypeError: Cannot read property 'load' of null
Uncaught TypeError: Cannot set property 'src' of null

Observing your code, it seems the element which setTrackList(t) is referring to doesn't exists in the DOM. I think the element you've declared for importing this script in your HTML (the <script> tag) is preceeding the <body> with your content. You have to get sure your DOM content is loaded before calling setTrackList(t), therefore you can bind the onload event to call this, else this function will not find the element.
Try this:
var reprod = new Player();
document.addEventListener("load", function() {
reprod.setTrackList(TRACKS).play();
});
function play() {
"use strict";
reprod.play();
document.getElementById("Play").innerHTML = "Pause";
}
function seguent() {
"use strict";
reprod.next();
document.getElementById("titol").innerHTML = "<b>Títol:</b>";
}
EDIT: Maybe I understood your problem: the context of the function which construct the class (function Player()) ends immediately after the calling itself, so variables are deleted. Therefore try declaring your variables in this context.
this.audio = yourValue;
Instead of:
var audio = yourValue;
EDIT: Maybe now it's working well, try this fiddle.
Except the error because of missing of tracks, it seems working.
Here's the code of the fiddle:
var TRACKS = [
{
id: 0,
nom: 'Johnny B. Goode',
autor: 'Chuck Berry',
src: 'src/johnnybgoode.mp3',
any: '1955'
},
{
id: 1,
nom: 'For Your Love',
autor: 'The Yardbirds',
src: 'src/foryourlove.mp3',
any: '1965'
}
];
var songs = 2;
var Player = function () {
this.getName = function () {
return this.currentPlaying.nom;
};
this.setTrack = function (obj) {
this.currentPlaying = obj;
this.source.src = obj.src;
this.audio.load();
this.audio.play();
return this;
};
this.setTrackList = function (t) {
this.trackList = t;
this.trackListPos = 0;
this.audio = document.getElementById('myAudio');
this.source = document.getElementById('audioSource');
this.setTrack(this.trackList[this.trackListPos]);
return this;
};
this.play = function () {
//this.audio.load();
this.audio.play();
return this;
};
this.stop = function () {
this.audio.pause();
this.audio.currentTime = 0;
return this;
};
this.pause = function () {
this.audio.pause();
};
this.next = function () {
if (this.currentPlaying.id === (songs - 1)) {
this.trackListPos = 0;
} else {
this.trackListPos += 1;
}
this.setTrack(this.trackList[this.trackListPos]);
};
};
//Initialize
var reprod = new Player();
if (document.getElementById("myAudio"))
reprod.setTrackList(TRACKS).play();
else
window.addEventListener('load', function() {
reprod.setTrackList(TRACKS).play();
});
window.play = function() {
"use strict";
reprod.play();
document.getElementById("Play").innerHTML = "Pause";
}
window.seguent = function() {
"use strict";
reprod.next();
document.getElementById("titol").innerHTML = "<b>Títol:</b>";
}
And here's the HTML:
<audio id="myAudio" controls="controls">
<source id="audioSource" src="">
Your browser does not support the audio format.
</audio>
<nav class="navegador" id="sidebar">
<div class="playerwrapper">
<div class="player">
<p id="titol"></p>
<p id="autor"></p>
<p id="any"></p>
<button type="button" onclick="window.seguent()">Següent</button>
<button id="Play" type="button" onclick="window.play()">Play</button>
</div>
</div>
</nav>

Related

What's the use of `this` in this example?

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.

How to make annyang detect no sound for 3 seconds or longer, then start next audio?

I got a person's help to build this so far. But my goal is after the first audio file, the program would still listen what the user says until the user finishes talking. And then if the program doesn't detect anything for 3 seconds(or longer), it will play the next audio. This program will do this over and over until all audio files have played.
However, there's one more case. If the user is a 2 years old kid, which means the kid might need to spend 2 seconds or longer between 2 sentences. In this case, the annyang might think the user has finished their sentences and play next audio. This way the program would interrupt the user speech. How should I handle this?
That person who gave me ideas of using setInverval and create some date objects and then minus date objects to get a time greater than 3 seconds, and play next audio. But it's not working. Is my logic of code wrong or there's a better way?
Any help I would appreciate it. Thank you in advance.
<script>
audio = new Audio();
if (annyang)
{
annyang.addCallback('start', function() {console.log('started listening!');});
annyang.addCallback('soundstart', function(){onSoundDetected();});
function monitorSound()
{
if(monitorId && monitorId > 0) return;
var monitorId = window.setInterval(function(){trackSound() }, 1000);
}
var lastSound= new Date();
function trackSound()
{
var now = new Date();
if ((now - lastSound) > 3000)
{
playNextAudio();
return;
}
}
function stopListening()
{
var monitorId = 0;
window.clearInterval(monitorId);
annyang.removeCallback('soundstart', onSoundHeard);
annyang.addCallback('result', function() {playNextAudio(); });
}
function onSoundHeard()
{
lastSound = new Date();
console.log(lastSound);
}
function playNextAudio()
{
if(audioIndex === playList.length - 1)
{
console.log("Played all audios");
return; // we have played all audio
}
else
{
annyang.addCallback('result', function() {
audio.src = dir + playList[audioIndex++] + extention;
audio.load();
//audio.ended = audio.play();
//audio.ended = setTimeout(function(){audio.play();}, 1500);
setTimeout(function(){audio.play();}, 1000);
});
}
}
function playFirstAudio()
{
audio.src = dir + playList[audioIndex] + extention;
audio.load();
audio.ended = setTimeout(function(){audio.play();}, 1000);
console.log('First audio is playing');
}
function onSoundDetected()
{
console.log('sound was detected');
playFirstAudio();
monitorSound();
}
// Start from here
var playList = ["1_hello", "2_how_old", "3_what_did_you_make"];
var dir = "sound/";
var extention = ".wav";
var audioIndex = 0;
annyang.debug(true);
};
</script>
You have logic errors in your code. When stopping and starting timers, you need to refer to the global variables.
This code isn't going to stop your timer:
function stopListening()
{
var monitorId = 0;
window.clearInterval(monitorId);
}
Your code also only initiates your play loop. It never starts the listening timer.
You logic will also play the first song twice.
To demonstrate the control flow, I have mocked the audio and annyang objects. The audio mock simulates the playing of 3 ten second audio files. When the window loads, the first audio mock will play. After which the annyang and the listening timer will start.
To mock annyang's sound detect there is a "mock sound" button. If it is clicked it will extend sound detection for another 3 seconds. Once 3 seconds goes by annyang will be paused and the next audio is played.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Annyang Mock</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
var AudioMock = function () {
this.src = null;
this.start = null;
this.timer = 0;
var that = this;
this.load = function () {
$('#AudioState').html("Loaded " + that.src);
}
this.play = function () {
that.start = new Date();
$('#AudioState').html("Playing: " + this.src);
window.setTimeout(that.end, 10000);
that.timer = window.setInterval(that.mockPlayTime, 500);
}
this.mockPlayTime = function () {
var d = new Date();
var elapsed = (d - that.start) / 1000;
$('#AudioState').html("Playing: " + that.src + " " + elapsed + " secs");
}
this.endMockPlayTime = function () {
window.clearInterval(that.timer);
}
this.end = function () {
that.endMockPlayTime();
$('#AudioState').html("Ended: " + that.src);
if (that.ended) {
that.ended();
}
}
this.ended = null;
};
var annyangMock = function () {
this.callbacks = {};
this.paused = false;
};
annyangMock.prototype.addCallback = function (name, callback) {
this.callbacks[name] = callback;
}
annyangMock.prototype.removeCallback = function (name, callback) {
this.callbacks[name] = null;
}
annyangMock.prototype.pause = function () {
$('#AnnyangState').html("Annyang: pause()");
this.paused = true;
}
annyangMock.prototype.start = function () {
$('#AnnyangState').html("Annyang: start()");
this.paused = false;
}
annyangMock.prototype.invoke = function (name) {
if (!this.paused) {
$('#AnnyangState').html("called(" + name + ")");
var cb = this.callbacks[name];
if (cb) cb();
}
}
annyangMock.prototype.debug = function (flag) { };
var annyang = new annyangMock();
var annyangMock = function () {
var callbacks = {};
var _paused = false;
var pause = function () {
$('#AnnyangState').html("Annyang: pause()");
_paused = true;
}
function addCallback(name, callback) {
callbacks[name] = callback;
}
function removeCallback(name, callback) {
callbacks[name] = null;
}
function invoke(name) {
$('#AnnyangState').html("called(" + name + ")");
if (name == "start") {
_paused = false;
}
else {
if (!_paused) {
var cb = callbacks[name];
if (cb) cb();
}
}
}
}
</script>
<script>
audio = new AudioMock();
var monitorId = 0;
if (annyang) {
annyang.addCallback('start', function () { console.log('started listening!'); });
annyang.addCallback('soundstart', onSoundHeard);
function monitorSound() {
lastSound = new Date();
if (monitorId && monitorId > 0) return;
monitorId = window.setInterval(trackSound, 1000);
annyang.start();
}
var lastSound = new Date();
function onSoundHeard() {
lastSound = new Date();
//console.log(lastSound);
}
function trackSound() {
var now = new Date();
var elapsed = now - lastSound;
$('#AnnyangState').html("Listening: " + (elapsed / 1000) + " secs");
if ((now - lastSound) > 3000) {
stopListening();
playNextAudio();
return;
}
}
function stopListening() {
window.clearInterval(monitorId);
monitorId = 0;
annyang.pause();
}
function playNextAudio() {
if (audioIndex === playList.length - 1) {
console.log("Played all audios");
return; // we have played all audio
}
else {
audio.src = dir + playList[++audioIndex] + extention;
load();
setTimeout(function () { play(); }, 1000);
}
}
function load() {
$($('#playlist li')[audioIndex]).addClass("loading");
audio.load();
}
function play() {
audio.play();
$('#playlist li').removeClass("loading")
var li = $('#playlist li')[audioIndex];
$(li).addClass("playing");
}
function playFirstAudio() {
annyang.pause();
audio.src = dir + playList[audioIndex] + extention;
load();
audio.ended = function () {
$('#playlist li').removeClass("playing");
lastSound = new Date(); // set timestamp
monitorSound(); // poll sound detection
}
setTimeout(function () { play(); }, 1000);
//console.log('First audio is playing');
}
// Start from here
var playList = ["1_hello", "2_how_old", "3_what_did_you_make"];
var dir = "sound/";
var extention = ".wav";
var audioIndex = 0;
annyang.debug(true);
$(document).ready(function () {
playFirstAudio();
var l = $("<ol>");
playList.forEach(function (j) {
l.append($("<li>").html(j));
});
$('#playlist').append(l);
})
};
</script>
<style type="text/css">
#playlist li {
width: 200px;
padding: 5px;
}
div {
padding: 15px;
}
#playlist li.playing {
border: 1px solid green;
background: #dedede;
}
#playlist li.loading {
border: 1px solid red;
background: #dedede;
}
</style>
</head>
<body>
<div>
<b>Annyang State:</b> <span id="AnnyangState"></span>
</div>
<div><b>Audio State:</b> <span id="AudioState"></span></div>
<div id="playlist">
<b>Playlist:</b>
</div>
<div id="Controls">
<input id="MockSound" type="button" value="Mock Sound" onclick="annyang.invoke('soundstart');" />
</div>
</body>
</html>

Calling a function which is inside another function

I am trying to call a function which is inside a Json from outside it. I want to trigger the "next" function on button "onClick" method. Here is my code. I tried calling it by onClick={this.next}, but it is never being called by this method.
export default class PlayerLogic extends Component{
componentDidMount() {
var self = this;
var player = videojs(this.refs.video, this.props.options).ready(function () {
self.player = this;
self.player.on('play', self.handlePlay);
});
player.markers({
onMarkerReached: function () {
player.pause();
},
next : function() {
// go to the next marker from current timestamp
console.log("reached")
var currentTime = player.currentTime();
for (var i = 0; i < markersList.length; i++) {
var markerTime = setting.markerTip.time(markersList[i]);
if (markerTime > currentTime) {
player.currentTime(markerTime);
break;
}
}
}
});
}
render() {
return (
<div>
<video {... props}>
<source src={this.props.src} type={this.props.type} id={this.props.id}/>
</video>
<button onClick={this.next}>Next</button>
</div>)
}
};
You have to refactor it with state further as below,
export default class PlayerLogic extends Component{
constructor() {
super();
this.state = {
player : {}
};
}
componentDidMount() {
var self = this;
var player = videojs(this.refs.video, this.props.options).ready(function () {
self.player = this;
self.player.on('play', self.handlePlay);
});
player.markers({
onMarkerReached: function () {
player.pause();
},
next : function() {
// go to the next marker from current timestamp
console.log("reached")
var currentTime = player.currentTime();
for (var i = 0; i < markersList.length; i++) {
var markerTime = setting.markerTip.time(markersList[i]);
if (markerTime > currentTime) {
player.currentTime(markerTime);
break;
}
}
}
});
this.setState({ player: player });
}
next() {
this.state.player.next ();
}
render() {
return (
<div>
<video {... props}>
<source src={this.props.src} type={this.props.type} id={this.props.id}/>
</video>
<button onClick={this.next.bind(this)}>Next</button>
I understand that you are using React, right! Is this .jsx or .tsx? Why you don't use arrow function. It's an ES6 feature. You can do this:
next: () => {
...
}
Mostly, all browser are supported with ES6. You can try this in your browser console:
let foo = () => 5;
console.log(foo());
Or you can compile your code back to ES5 using babel:
https://babeljs.io/docs/learn-es2015/
The problem here is when you use function, javascript will assume that this.next is your button next method, not your player next method. Arrow function () => {} will preserve this as you did:
var self = this;
var callback = function() {
self.doSomethingHere();
}
var player = video(...).ready(callback);
So you just has to:
let callback = () => {
this.doSomethingHere();
}

How to play songs without refreshing the web page?

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({ ... });

Youtube api does not load on firefox

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.

Categories