Cannot destroy youtube embedded video player - javascript

I am not able to destroy embedded youtube video on plyr player. player.destroy() method is called without any error but it does not destroy the player.
For this reason when I try to open another embedded video, it just loads the previous embedded video.
When I checked isEmbed property of the player it returns false. It should be true as I am playing youtube video.
I am using react js.
let video = document.getElementById('player-embed');
var youtubeEmbedId = extractYoutubeEmbedId(options.source.src)
window.player = new Plyr(video, defaultOptions);
useEffect(() => {
return () => {
window.player.destroy();
if (window.hls) {
window.destroyHLS();
}
}
}, [])
return (
<div id="player-embed" data-plyr-provider="youtube" data-plyr-embed-id={youtubeEmbedId}/>
);
I found this codepen example for plyr youtube video example and it is working as expected. isEmbed property returns true and player.destroy() method indeed destroy the player. Don't know why it's not working for me.

It seems like window.player = new Plyr() is called everytime your react component is rerendered, therefore you keep creating new Plyr instances. Try moving Plyr initialization to useState like const [player] = useState(new Plyr()) and then assign it to window in useEffect so it would get called only once.
It still might have some bugs so you would be better off using Plyr official package for react https://github.com/chintan9/plyr-react

Related

Why isn't clicking on an audio track immediately triggering play?

I have a StackBlitz minimum code example which illustrates the problem. For brevity, I've also placed the problematic code below.
Whenever the user clicks on a track of a number of tracks, I want the Audio component to immediately play that track, think Spotify etc.
Currently, the source of the audio component is updating, but it does not play the track unless there is a triple click.
I'm not sure why a triple click is needed to create a successful play call.
const changeSource = newSource => {
setSource(newSource);
togglePlayPause();
};
Here is the setSource hook, which is initialised to a default track:
const [source, setSource] = useState(
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-4.mp3'
);
const togglePlayPause = () => {
const prevValue = isPlaying;
setIsPlaying(!prevValue);
if (!prevValue) {
audioPlayer.current.play();
} else {
audioPlayer.current.pause();
}
};
It'll do that, unless you preload your audio data. The <audio> element would need to go out, bring the data into the browser, process it, then begin to play. There is a way to try to preload the data. By setting <audio preload="auto"> it will "attempt" to preload tracks on page load. But, when dynamically setting source, you can't really get that data until you know what the source is. You can get around this, a little, by using the autoplay attribute, which will cause the browser to automatically begin fetching the source once it is set. (But then, it will also start playing it right away as well.)

Angular: I have to mute browser tab through javascript. There are no. of audio objects in all components, So I need common script to pause app audios

Angular: I have to mute browser tab through javascript. There are no. of audio objects in all components, So I need common script to pause app audios.
Number of audio objects in each component with play & pause operations. But now I want to add button through which I can mute the browsers tab. Also If I want then I can enable it.
My current component object like (code snippet as example):
//before onInit & constructor function
audioLogin = new Audio();
//in login page loading function
this.audioLogin.play();
Like above there are no. of such objects in all components. I have to add toogle button to on/off app sounds. For that I need to mute/unmute browsers tab on that button click.
Please suggest me script (javascript/jquery) or ideas.
Create a service which can create any media elements and keep track of it.
Use the service to create instances of media elements.
Call the service function to mute all the media created by the service.
Since as you said you have already created multiple audio/video tags. You just need to refactor a little bit to make this work.
The main thing to note here is "dont create direct new Audio()" you cannot keep track of what you created. just move the creation logic to a service and call the service method.
// Create a service class to handle media creations
class MediaService {
media = []; // will hold all media references when created
createNewAudio() {
// create a Audio instance as usual.
const audio = new Audio();
// push the reference of the instance to media array
// to perform any operation in future.
this.media.push(audio);
// return the Audio instance created above.
return audio;
}
createNewVideo() {
// create a Video instance as usual.
const video = new Video();
// push the reference of the instance to media array
// to perform any operation in future.
this.media.push(video);
// return the Video instance created above.
return video;
}
muteAllMedia() {
this.media.map(m => m.pause());
}
}
// Inject and Use the service to create any media elements
class MyComponent {
constructor(private mediaService: MediaService) { }
ngOnInit() {
//in onInit function
this.audioLogin = this.mediaService.createNewAudio();
//in login page loading function
this.audioLogin.play();
}
mute() {
this.mediaService.muteAllMedia();
}
}

How do I get audio to loop more smoothly in React?

The problem
I'm trying to loop some audio in a React app created with create-react-app using useEffect. I've got it working but there's a short delay between the audio ending and re-starting. I want to use the audio as backing tracks to play guitar to so I need it to loop perfectly smoothly. I'm confident that the track length is correct, I recorded it myself and exported exactly 8 bars, and it loops fine in iTunes.
Current code
Thanks to the accepted answer in this question, my audio player function works fine, and currently looks like this:
import React, { useState, useEffect } from 'react'
const useAudio = audioPath => {
const [audio] = useState(new Audio(audioPath))
const [playing, setPlaying] = useState(false)
const toggle = () => setPlaying(!playing)
useEffect(() => {
playing ? audio.play() : audio.pause()
},
[playing, audio]
)
useEffect(() => {
audio.addEventListener('ended', () => {
audio.currentTime = 0
audio.play()
setPlaying(true)
})
}, [audio])
return [playing, toggle]
}
const Player = ({ audioPath }) => {
const [playing, toggle] = useAudio(audioPath)
return (
<div>
<button onClick={toggle}>{playing ? 'Pause' : 'Play'}</button>
</div>
)
}
export default Player
The audioPath is passed in and is just a relative path, that loads fine. It plays fine, it pauses fine, it does loop, just with a tiny delay between loops.
What I've tried
As you can see from the code, I've been trying to hijack the audio ended event and setting the audio back to the start of the track but obviously this isn't instant - I'm not really sure how to handle this. I've tried in my first useEffect function checking the time of the audio and if it's within say 500ms of the end of the track setting the time back to 0 but I couldn't get that working, and it seems very hacky and unreliable anyway. Ideally I'm after a proper solution that will work with any tracks as I want to add more.
Demo
Go to the very bottom of the GitHub pages site where this is hosted, expand the very bottom panel and hit play.
Ive been playing around with audio a bit recently as well and found that the react-h5-audio-player npm package was the best option for me.
Its got good storyboard examples which include looping and custom controls etc which you might just be able to hide the display by using CSS if you want to keep the single play/pause button you currently have.

Embed.ly API: player.js not working with youku.com

I have a problem with embedded video. I use "Embed.ly API" and "player.js":
https://docs.embed.ly/docs/oembed
https://docs.embed.ly/docs/playerjs
I'm trying to embed a video from "youku.com" on my site. The "youku.com" is a content provider, as stated here: https://embed.ly/providers
Video is added but player's methods (play, pause and others) does not work for video. But if I changing video service from "youku.com" to "youtube.com" and use video from "youtube.com", then methods works well.
I also tried to connect the "platform.js" instead of the player. The result is the same.
Maybe the "player.js" worked before, I guess that the "youku.com" has changed its API, but the "player.js" did not track changes.
Does the player.js really work with "youku.com" now? Perhaps something should be added to my code for solve the problem? I am not in China, can it have any effect due to the fact that "youku.com" is a Chinese service?
Thanks.
My code js: (And another question: are there any errors in the code?)
var obj_json = $.getJSON('https://api.embedly.com/1/oembed?' + $.param({
url: :url, // for example: https://v.youku.com/v_show/id_XMzg2MjgwNzA0OA==.html
key: :key // my_API_key
})).done(function () {
$('.embeded').html(obj_json.responseJSON.html).find('iframe.embedly-embed').each(function () {
// initialize the player.
var player = new playerjs.Player(this);
player.on('ready', function () {
player.setLoop(true); // it is not work
player.play(); // it is not work
//..................................
// There are also special buttons on the page, clicking on which the video should start or stop:
$('.button_play').click(function () {
player.play(); // pressing occurs, but player it is not work
});
$('.button_pause').click(function () {
player.pause(); // pressing occurs, but player it is not work
});
//.........................................
});
});
});

mediaElementjs: how to get instance of the player

I'm stuck with a little problem with MediaElement.js player.
To get the instance of the player, I do this (works with html5 compatible browser):
// Get player
this.playerId = $('div#shotlist-player video').attr('id');
this.player = window[this.playerId];
But it's not working as soon as it fallback in flash. In fact, it's not working because I'm not calling an instance of MediaElement itself. But I don't see how I can call it.
The player is created with
$('video').mediaelementplayer({....});
How can I get the mediaelement object?
------------EDIT----------------
Ok I finally found how to make it works:
// Get player
mePlayer = $('div#shotlist-player video.video-js')[0];
this.player = new MediaElementPlayer(mePlayer);
Now I can user mediaElement instance correctly.
This post is a lot of speculation, but may be correct. Docs are lacking (;
The answer by sidonaldson is perfectly acceptable if you wish to create a new MediaElement instance and get a handle on it. If there's one already present, it seems to try to reinitialize another instance on that element and freaks out.
I am pretty sure mediaelement.js augments the builtin HTML5 controls by providing a JavaScript API to manipulate Flash/Silverlight players via those elements. I may be wrong, but other advice I've seen on this issue in multiple places is to do something like:
$playButton.click(function() {
$('video, audio').each(function() {
$(this)[0].player.play();
});
});
To create a play button as an external DOM element which will fire off all players on the page. This indicates to me that the implementation is something like I've described.
Try:
var player = $('video').mediaelementplayer({
success: function (me) {
me.play();
}
});
// then you can use player.id to return the id
// or player.play();

Categories