Currently trying to detect when a YouTube video in a playlist ends so I can play the next one ( Adding event handler to loop through dynamically created playlist (YouTube API) ). I'm now trying to figure out when one variable is equal to another, so I can navigate to the next video in the playlist.
Here is the code to detect when a video has ended and save the video ID to a div (stopID):
function stopCycle(event) {
if (event.data == YT.PlayerState.ENDED) {
var url = event.target.getVideoUrl();
var match = url.match(/[?&]v=([^&]+)/);
var videoId = match[1];
$('#stopID').html(videoId);
}
}
And here is my attempt at detecting (and testing with an alert) when the video ID of the video clicked equals the video ID of the in stopCycle (i.e. when the video ends):
$('#Playlist').on('click', '.playlistYT', function(){
$('#subHistory').click();
videoID = $(this).attr("src").slice(27,38);
$('#infoID').val(videoID);
player.loadVideoById(videoID );
var nextVideo = $(this).parent().next().find('.playlistYT');
$.when($('#stopID').html() == videoID).then(function(){
alert("asd");
});
Currently, I get the alert immediately, i.e. before the stopID div even has a video ID in it. Where am I going wrong with $.when function? Should I be sticking to an if/else statement?
$.when takes in a promise argument. Just use an if conditional to check equality.
Related
I'm building an online 'TV' which will use YouTube live-streams for multiple channels.
The channels are contained within tabs. The videos need to be stopped when changing tabs otherwise you can hear the audio in the background.
Here's a link to the JSFiddle: https://jsfiddle.net/matlow/08k4csuh/
I've managed to turn the 'Channel 1' off when changing to another channel with:
var iframe = document.getElementsByClassName("tvscreen")[0].contentWindow;
and
iframe.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
In the tab javascript for loop which also handles the tabcontent[i].style.display = "none";
I think I need to use the for loop to call each instance of the iframe... but I'm quite new to javascript so I'm not quite sure how to achieve this.
It will also help to use iframe.postMessage('{"event":"command","func":"playVideo","args":""}', '*'); so the video plays automatically again when clicking on the relevant tab... but again I'm not quite sure how to implement this.
I've been working on this for a few days so if anyone had any tips or pointers I would really appreciate it!
Thanks for reading! :)
You are not using YouTube's API properly. See https://developers.google.com/youtube/iframe_api_reference
In your fiddle, programmatic play is not possible, because you can't know when the YouTube player is ready, as you are not the one initialising it. Your attempts to play the video might take place too early.
Programmatic pause (you managed to pause the first video) is possible thanks to enablejsapi=1 in the iframe src and the fact that the player is ready at that point.
Here's a fork of your fiddle - https://jsfiddle.net/raven0us/ancr2fgz
I added a couple of comments. Check those out.
// load YouTube iframe API as soon as possible, taken from their docs
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);
// initialised players are kept here so we don't have to destroy and reinit
var ytPlayers = {};
function mountChannel(channel) {
var player;
var iframeContainer = document.querySelectorAll('#' + channel + ' iframe');
// if the channel & iframe we want to "mount" exist, check for playing iframes before doing anything else
if (iframeContainer.length > 0) {
// Object.keys() is ECMA 5+, sorry about this, but no easy to check if an object is empty
// alternatively, you could have an array, but in that case, you won't be able to fetch a specific player as fast
// if you don't need that functionality, array is as good cause you will just loop through active players and destroy them
var activePlayersKeys = Object.keys(ytPlayers);
if (activePlayersKeys.length > 0) { // if players exist in the pool, destroy them
for (var i = 0; i < activePlayersKeys.length; i++) {
var activeChannel = activePlayersKeys[i];
var activePlayer = ytPlayers[activeChannel];
activePlayer.getIframe().classList.remove('playing'); // mark pause accordingly, by removing class, not necessary
activePlayer.pauseVideo();
}
}
// check if player already initialised and if player exists, check if it has resumeVideo as a function
if (ytPlayers.hasOwnProperty(channel)) {
ytPlayers[channel].playVideo();
} else {
var iframe = iframeContainer[0];
player = new YT.Player(iframe, {
events: {
'onReady': function (event) {
// event.target is the YT player
// get the actual DOM node iframe nad mark it as playing via a class, styling purposes, not necessary
event.target.getIframe().classList.add('playing');
// play the video
event.target.playVideo();
// video may not autoplay all the time in Chrome, despite its state being cued and this event getting triggered, this happens due to a lot of factors
},
// you should also implement `onStateChange` in order to track video state (as a result of user actions directly via YouTube controls) - https://developers.google.com/youtube/iframe_api_reference#Events
}
});
// append to the list
ytPlayers[channel] = player;
}
}
}
// Get the element with id="defaultOpen" and click on it
function onYouTubeIframeAPIReady() {
// YouTube API will call this when it's ready, only then attempt to "mount" the initial channel
document.getElementById("defaultOpen").click();
}
This is the first time I worked with YouTube's iframe API, but it seems reasonable.
I am working with an iframe that contains code that we receive from a third party. This third party code contains a Canvas and contains a game created using Phaser.
I am looking for a way to mute the sound that this game does at some point.
We usually do it this way:
function mute(node) {
// search for audio elements within the iframe
// for each audio element,(video, audio) attempt to mute it
const videoEls = node.getElementsByTagName('video');
for (let i = 0; i < videoEls.length; i += 1) {
videoEls[i].muted = true;
}
const audioEls = node.getElementsByTagName('audio');
for (let j = 0; j < audioEls.length; j += 1) {
audioEls[j].muted = true;
}
}
After some research I found out that you can play sound in a web page using new Audio([url]) and then call the play method on the created object.
The issue with the mute function that we use is that, if the sound is created with new Audio([url]), it does not pick it up.
Is there a way from the container to list all the Audio elements that have been created within a document or is it just impossible, and that creates a way to play audio without the possibility for iframe container to mute it?
No, there is no way.
Not only can they use non appended <audio> elements like you guessed, but they can also use the Web Audio API (which I think phaser does) and for neither you have a way of accessing it from outside if they didn't expose such an option.
Your best move would be to ask the developer of this game that it exposes an API where you would be able to control this.
For instance, it could be some query-parameter in the URL ( https://thegame.url?muted=true) or even an API based on the Message API, where you'd be able to do iframe.contentWindow.postMessage({muted: true}) from your own page.
So I'm having a little trouble with videos, i have a website and there i have the same video displayed in 3 different pages.
The videos are all paused until the user clicks on them to start.
The problem is when, i leave a page and the video is just there even if i have clicked on play or pause or haven't done anything at all, the other two, give me an error saying vid.pause is not a function.
This is the HTML part ->
<video id="{{vid?.id}}" src={{vid?.video}} onloadedmetadata="this.paused = true; this.currentTime = 1" (click)="playPause(vid)"> </video>
And the js ->
playPause(vid) {
var vid = (<HTMLMediaElement>document.getElementById(vid.id));
if (vid.paused == true) {
vid.play();
} else {
vid.pause();
vid.currentTime = 1;
}
}
The problem for me was that I tried pausing a jQuery object, but that's not possible. You need to pause a DOM element. So I corrected it by converting my jQuery object to a DOM element.
BEFORE:
$("#video").pause();
AFTER / SOLVED:
$("#video)[0].pause();
UPDATE:
One thing to notice is:
You are passing the vid property to the element and then back to the .ts. This is not good practices.
Also, you are using variables inside properties the wrong way.
The best way to do this would be:
.html file
<video #myVideo [id]="vid?.id" [src]="vid?.video" onloadedmetadata="this.paused = true; this.currentTime = 1" (click)="playPause(myVideo)"> </video>
.ts file
playPause(vid: HTMLMediaElement) {
if (vid.paused == true) {
vid.play();
} else {
vid.pause();
vid.currentTime = 1;
}
}
My guess is you have a difference in your HTML and JS files.
In your HTML file, you have the video id as vid.id.
In your JS file, you are using getElementById, passing the id as vid.video.
Therefore var vid will be undefined and vid.pause() won't be a function.
You probably have to use var vid = (<HTMLMediaElement>document.getElementById(vid.id));
Alright, I have very little programming experience and just want to make controls for video and audio pieces together on one page. I have searched a lot and can't figure it out.
var stop = function (id) {
.pause();
.src = '';
};
I want to pass either the audio or videos' id as the parameter in my function but I don't understand how make code that can be used with either. I can't put id.pause(); into my function to pause whatever id I put in as a parameter, so I'm confused as to how to make it work. Any help is appreciated!!
You could use JQuery:
$('#playMovie1').click(function(){
$('#movie1').get(0).play();
});
Or you could do something like this:
function playVid(x) {
var vid = document.getElementById(x).pause();
}
You can use
var stop = function (htmlId) {
var vid = document.getElementById(htmlId);
vid.pause();
};
If you are passing the id then use that id to pause or play
var stop = function (id) {
$('#'+id).pause(); // to pause the video
.src = '';
};
I have an app that tracks video views and integrates it with other marketing activities. In doing so, I needed to keep track of how long a person watches a html5 video and post it back to my app (via an API). I'm using videojs player, but really this is just a wrapper around the HTML5's api for this attribute. This is in an app with various videos can be loaded based on what page they are watching, so I needed a solution that tracked regardless of video length.
The problem I had, as a video plays the API reports back every ~300MS and I didn't want to hit my API that often. So I needed a solution to keep track of last time I posted. After digging around, I couldn't find an answer, so in case someone else with a similar need, my solution to this problem is below.
We've decided that I wanted to post my video viewing results every 5 seconds, but since we have no guarantee that the currentTime will report back at exactly 5 seconds, so we just need to round to closest whole integer value.
On my video wrapper div, I've added a data attribute called data-last-time-push. I post the rounded time every time I push and check to see if we have exceed the interval before we post again.
HTML
<div id="video-wrapper" data-time-last-push="0">
Javascript
Bind the videojs container to the timeupdate property.
var vid = videojs("video-container", {}, function() {
this.on('timeupdate', videoTracker);
});
function for posting ajax...
var videoTracker = function() {
var player = this;
var last_push, wrapper, current;
wrapper = $('#video-wrapper');
last_push = wrapper.attr("data-time-last-push");
current = Math.round(player.currentTime());
//you could make the 5 here to be a variable or your own interval...
if (current%5 === 0) {
if (current > last_push) {
//do your AJAX post here...
wrapper.attr("data-time-last-push", current);
console.log('currentTime = ' + player.currentTime());
console.log(' duration: ' + player.duration());
}
}
};
Note, I tried to do a jsfiddle to show it working, but ended up running into HTTPS videos because the sample videos don't work through secure connections.