I'm trying to load a YT video via the api. This official tutorial snippet works basically out of the box when I output it directly onto a page. It's a different story when I try and incorporate it into my JS workflow with modules / webpack.
Questions:
How do I make these methods (play / mute / setLoop / stop / etc) available globally?
How do I create the script tag first and then run the onYouTubeIframeAPIReady function?
Thanks all. Hope this isn't a completely useless question!
<script>
// 1. Load up the script tag
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 2. Creates an <iframe> (and YouTube player) after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('a-spot-video-background', {
width: '1280',
height: '720',
videoId: 'T8v-gQeQZJY',
playerVars: {
autoplay: 1,
controls: 0,
modestbranding: 1,
rel: 0,
showinfo: 0,
loop: 1,
disablekb: 1,
color: 'white'
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 3. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.mute();
event.target.playVideo();
event.target.setLoop();
}
// 5. The API calls this function when the player's state changes. The function indicates that when playing a video (state=1),
// the player should play for six seconds and then stop.
var done = false;
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
//setTimeout(stopVideo, 10000);
player.playVideo();
done = true;
}
}
function stopVideo() {
player.stopVideo();
}
</script>
First question is answered in comment. This answers to second one:
function loadScript(scriptUrl, globalName) {
if (globalName && window[globalName])
return Promise.resolve();
return new Promise((resolve, reject) => {
let scr = document.createElement('script');
scr.type = "text/javascript";
scr.src = scriptUrl;
document.getElementsByTagName('head')[0].appendChild(scr);
scr.onload = (() => {
!globalName || window[globalName] ?
resolve() : reject(Error('window.' + globalName + ' undefined'));
});
scr.onerror = () => reject(Error('Error loading ' + globalName||scriptUrl));
});
}
var url = "https://code.jquery.com/jquery-3.3.1.min.js";
loadScript(url, "$")
.then(() => console.log('jQuery loaded. Now I can call any jQuery method.'));
or you can use just script.onload event listener without promise. I like promise version because it gives you some additional options and has clear flow (without nesting or referencing across whole file).
Related
I have a working player in videojs and I'm trying to get the current position of the progress bar, In the documentation provided by videojs they use myPlayer.currentTime() to get and set the time in seconds but it is not working for me.
My code:
var tag = document.createElement("script");
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player("player", {
host: "https://www.youtube.com",
/* no need to specify player
size here since it is handled
by the player-size div */
videoId: video_id,
playerVars: {
enablejsapi: 1,
playsinline: 1,
start: 0,
disablekb: 0
},
events: {
onReady: onPlayerReady,
onStateChange: onPlayerStateChange
}
});
}
function onPlayerStateChange(event) {
console.log("player state: " + player.getPlayerState());
if (player.getPlayerState() == 2){
console.log("STOPED AT:" + player.currentTime()); // This is where i want to log the current time of the playing video
}
}
function updateVideoId(video_id) {
player.loadVideoById(video_id, -1);
}
function stopVideo() {
player.stopVideo();
}
function onPlayerReady(event) {
document.getElementById("play").addEventListener("click", function() {
player.playVideo();
});
}
What am I doing wrong?
The code above is YouTube's API, not Video.js.
If you want to play a YouTube video in Video.js and use Video.js's API, you need to use the YouTube tech for Video.js
In my project, I am using Youtube videos embeds (not my videos) and I want the videos to show video ads which is a normal behavior of youtube player.
I am using Iframe API.
<div id="player"></div>
<script type="text/javascript">
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '720',
width: '1080',
videoId: '0-7IHOXkiV8',
host: 'https://www.youtube-nocookie.com',
playerVars: {
'fs': 1,
'modestbranding': 0,
'playsinline': 0,
'rel': 0,
'enablejsapi': 1,
},
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
// event.target.playVideo();
}
// 5. The API calls this function when the player's state changes.
// The function indicates that when playing a video (state=1),
var done = false;
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
// setTimeout(stopVideo, 6000);
// done = true;
}
}
function stopVideo() {
player.stopVideo();
}
I want to use Iframe API and show ads. Is there something I am missing here to implement such functionality?
This is working just fine, however it seems to only auto play every now and then. When I try to load the website through incognito it doesn't auto play at all:
I'm wondering if it is even the code or it's just because YouTube is limiting something
(function(window, document, undefined) {
'use strict';
function removeElement(array, element) {
return array.filter(function(el) {
return el !== element
});
}
function changeVideo() {
if (player.getCurrentTime() >= delay) {
var currentVideo = player.getVideoData().video_id,
randomVideo = removeElement(videoPlaylist, currentVideo)[Math.floor(Math.random() * (videoPlaylist.length - 1))];
player.loadVideoById(randomVideo);
}
}
function onPlayerStateChange(event) {
clearInterval(repeat);
if (event.data === 1) {
repeat = setInterval(changeVideo, 500);
}
}
window.onYouTubeIframeAPIReady = function() {
var randomVideo = videoPlaylist[Math.floor(Math.random() * videoPlaylist.length)];
player = new YT.Player('player', {
height: '315',
width: '560',
videoId: randomVideo,
playerVars: {
'autoplay': 1,
'controls': 0,
'showinfo': 0,
'iv_load_policy': 3
},
events: {
'onStateChange': onPlayerStateChange
}
});
}
var tag = document.createElement('script'),
player,
videoPlaylist = ['wfF0zHeU3Zs', 'WNcsUNKlAKw', '9E6b3swbnWg'],
delay = 10, //s
repeat;
tag.src = 'https://www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
})(window, document);
Browsers are blocking auto play for when the sound is on.
https://www.theverge.com/2018/5/3/17251104/google-chrome-66-autoplay-sound-videos-mute
https://www.theverge.com/2019/3/19/18272377/firefox-66-release-date-news-features-autoplaying-videos
A way around this might be to mute the video until the user moves their mouse over the video. (I think this is a common solution but correct me if I'm wrong)
You also have to think about the fact that users might not want to have the video autoplaying depending on what kind of website this is implemented into.
The reason it sometimes seems to be working for you is because chrome is learning that you want audio on the website you're creating but for example for me it is never playing.
I'm currently loading a YouTube video inside a -UIwebView, this is working perfectly and the video starts playing automatically. but the problem is that once the video finishes inside the -UIwebViewit just stops and shows the play button. I would however want the video player to close so the users wouldn't have to manually hit the "Done" button every time.
Image of the screen when the video doesn't close.
Here's the link I launch the video inside the -UIwebview
NSString *youTubeVideoHTML = [NSString stringWithFormat:#"<!DOCTYPE html><html><head>
<style>body{margin:0px 0px 0px 0px;}</style></head> <body> <div id=\"player\"></div> <script>
var tag = document.createElement('script'); tag.src = \"http://www.youtube.com/player_api\";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
width:'320',
height:'200',
videoId:'%#',
events: {
'onReady': onPlayerReady,
}
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
</script> </body> </html>", [[NSUserDefaults standardUserDefaults]
objectForKey:#"detailVideoURL"]];
EDIT:
I tried implementing the first answer into my code and this is how it ended up like. I'm still unsure what I should add into the callback ?
NSString *youTubeVideoHTML = [NSString stringWithFormat:#"<!DOCTYPE html><html><head><style>body{margin:0px 0px 0px 0px;}</style></head> <body> <div id=\"player\"></div> <script>
var tag = document.createElement('script'); tag.src = \"http://www.youtube.com/player_api\";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
width:'320',
height:'200',
videoId:'%#',
events: {
'onReady': onPlayerReady,
}
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000); done = true;
}
if (event.data == YT.PlayerState.ENDED) {
window.location = "callback:nil"; }; }
function stopVideo() {
player.stopVideo();
}
</script> </body> </html>", [[NSUserDefaults standardUserDefaults] objectForKey:#"detailVideoURL"];
Image of the error : http://gyazo.com/31da955d8af98f40b82789a7f60c1edb
You can do it by calling your objective function from javascript using youtube api.
On the HTML/Javascript Side
<html>
<body>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
<script>
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '300',
width: '250',
videoId: 'GUdYebAN-Fs',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
}
// 5. The API calls this function when the player's state changes.
// The function indicates that when playing a video (state=1),
// the player should play for six seconds and then stop.
var done = false;
function onPlayerStateChange(event) {
event.target.destroy();
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 60000);
done = true;
}
if (event.data == YT.PlayerState.ENDED) {
event.target.destroy();//to close the video
window.location = "callback:anything"; //here's the key
};
}
function stopVideo() {
player.stopVideo();
}
</script>
</body>
</html>
onStateChange- From the google's API reference page
This event fires whenever the player's state changes. The data property of the event object that the API passes to your event listener function will specify an integer that corresponds to the new player state.
So in the javascript function you would be able to trace the End of the video time.Then just
add event.target.destroy() on entering the state end condition.
On the native/iOS side:
And even you can do the native stuff here by catching up the call back method using the webview delegate method.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if ( [[[request URL] scheme] isEqualToString:#"callback"] ) {
//Video has ended up playing,you can perform native stuff here...
return NO;
}
return YES;
}
Note: Please make sure to test this code in your device,because sometimes the simulators won't be supporting.
Good Luck...Hope it helps...
This is how you can apply Puneeth's solution to Google's YTPlayerView so the player hides after the video finishes:
- (void)playerView:(YTPlayerView *)playerView didChangeToState:(YTPlayerState)state
{
if(state == kYTPlayerStateEnded) {
[self.youTubePlayer.webView stringByEvaluatingJavaScriptFromString:#"player.destroy();"];
// you'll have to recreate the player again since it's now destroyed
[self.youTubePlayer removeFromSuperview];
self.youTubePlayer = nil;
}
}
I am using the iFrame Youtube API with the following code. I call this view with Ajax to render it and append it in a div.yt-player within my page. It works the first time I call the view, but after I close the video (it empties the div.yt-player) and click on another link that calls my view, the video doesn't load at all (blank). I've been struggling and still don't see why it happens, especially that it works the first time. Any kind of help would be much appreciated.. Thanks.
PS: Both the html and the javascript are rendered by the view.
Html
<div id="player"></div>
Javascript:
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '486',
width: '864',
videoId: '#{#media['youtube_url']}',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
},
playerVars: {
'showinfo': 0,
'iv_load_policy': 3,
'color': 'white',
'fs': 1,
'autoplay': 1,
'vq': 'hd720'
}
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
var done = false;
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING && !done) {
setTimeout(stopVideo, 6000);
done = true;
}
}
function stopVideo() {
player.stopVideo();
}
Just check if the script has been defined.
if(document.getElementById('iframe_api') === null){
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
tag.id = "iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
else
runPlayer();
function runPlayer(){
var player;
player = new YT.Player(element.children()[0], {
playerVars: {
autoplay: 1,
html5: 1,
controls: 1,
showsearch: 0,
showinfo: 0
},
height: scope.height,
width: scope.width,
videoId: scope.videoid,
events: {
onStateChange: function (event) {
if (event.data == YT.PlayerState.ENDED) {
scope.$emit('VideoEnded');
}
}
}
});
}
$window.onYouTubeIframeAPIReady = function () {
runPlayer();
};
I debugged the problem. onYouTubeIframeAPIReady() is called only the first time the YouTube API is loaded (when the user refreshes the page and clicks on the link to the view for example), and because I'm calling my views in Ajax, it didn't get fired up the following times.
So I replaced the first block of code by wrapping it in a conditional:
if (typeof youtube_api_init == 'undefined') {
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
and
if (typeof youtube_api_init != 'undefined') {
onYouTubeIframeAPIReady();
}
and at the end of the script, I set the youtube_api_init for the browser to remember the YouTube API has already been loaded:
var youtube_api_init = 1;
PS: the first time I tried, I called my variable yt_api_init instead of youtube_api_init, and it didn't work cause it happens that's a name YouTube already uses in its api...