I am using a Wordpress plugin to add timestamp links of videos that will seek the video automatically to a certain timeframe.
Javascript:
function onYouTubeIframeAPIReady(){
console.log('Confirmation of call to onYouTubeIframeAPIReady()');
var STT = {
settings: STTSettings,
media: undefined,
skipTo: undefined,
isHTML5: false,
isYoutube: true,
doHTML5Skip: function() {
STT.media.removeEventListener('canplaythrough', STT.doHTML5Skip);
STT.media.currentTime = STT.skipTo;
STT.media.play();
},
doYoutubeSkip: function() {
STT.media.seekTo(STT.skipTo);
STT.media.playVideo();
}
};
STTSkipTo = function(time) {
var audio = document.getElementsByTagName('audio'),
video = document.getElementsByTagName('video'),
iframe = document.getElementsByTagName('iframe'),
timeArray = time.split(':').reverse(),
seconds = parseInt(timeArray[0]),
minutes = timeArray.length > 1 ? parseInt(timeArray[1]) : 0,
hours = timeArray.length > 2 ? parseInt(timeArray[2]) : 0;
STT.skipTo = seconds + (minutes * 60) + (hours * 3600);
if (STT.media) {
console.log(STT.media.seekTo);
STT.doSkip();
return;
}
if ((parseInt(STT.settings.link_audio) && audio.length) ||
(parseInt(STT.settings.link_video) && video.length))
{
STT.doSkip = STT.doHTML5Skip;
if (parseInt(STT.settings.link_audio) && audio.length) {
STT.media = audio[0];
} else {
STT.media = video[0];
}
STT.media.addEventListener('canplaythrough', STT.doHTML5Skip);
STT.media.load();
STT.media.play();
return;
} else if (parseInt(STT.settings.link_youtube && iframe.length)) {
// Inspect the iframes, looking for a src with youtube in the URI
for (var i = 0; i < iframe.length; i++) {
if (iframe[i].src.search('youtube') !== -1) {
// Set up the JS interface
STT.doSkip = STT.doYoutubeSkip;
iframe[0].id = 'stt-youtube-player';
STT.media = new YT.Player('stt-youtube-player', {
events: {
onReady: STT.doYoutubeSkip
}
});
return;
}
}
}
console.log('Skip to Timestamp: No media player found!');
return;
}
}
On my localhost, the plugin works seamlessly but on my hosted website, I get the following error with the stack as follows:
Uncaught TypeError: STT.media.seekTo is not a function
I think for some reason the website is unable to load the www-widgetapi.js which is a dependency for YouTube iframe API and thus is unable to generate the required function definition. However, I did try to include the script manually in the header but it still didn't work.
If anyone knows of any other wordpress plugin, please advice.
Based from this documentation, you need to set both the two parameter of the player.seekTo(seconds:Number, allowSeekAhead:Boolean).
Seeks to a specified time in the video. If the player is paused when the function is called, it will remain paused. If the function is called from another state (playing, video cued, etc.), the player will play the video.
The seconds parameter identifies the time to which the player should advance.
The player will advance to the closest keyframe before that time unless the player has already downloaded the portion of the video to which the user is seeking.
The allowSeekAhead parameter determines whether the player will make a new request to the server if the seconds parameter specifies a time outside of the currently buffered video data.
It should be like: Player.seekTo(120, true)//120 seconds
Related
I have a html5 <audio> tag in page, but how can I know its duration time?
<audio controls="">
<source src="p4.2.mp3">
</audio>
2020 solution:
You will get undefined or NaN (not a number) when the audio metadata isn't loaded yet. Therefore some people suggested to use onloadedmetadata to make sure the metadata of the audio file is fetched first. Also, what most people didn't mention is that you have to target the first index of the audio DOM element with [0] like this:
-- Vanilla Javascript:
var audio = document.getElementById('audio-1');
audio.onloadedmetadata = function() {
alert(audio.duration);
};
If this won't work try this, however not so reliable and dependent on users connection:
setTimeout(function () {
var audio = document.getElementById('audio-1');
console.log("audio", audio.duration);
}, 100);
-- JQuery:
$(document).ready(function() {
var audio = $("#audio-1")[0];
$("#audio-1").on("loadedmetadata", function() {
alert(audio.duration);
});
});
var au = document.createElement('audio');
au.addEventListener('loadedmetadata',function(){
au.setAttribute('data-time',au.duration);
},false);
In a comment above, it was mentioned that the solution is to bind an event handle to the event loadedmetadata. This is how I did that -
audio.onloadedmetadata = function() {
alert(audio.duration);
};
I was struggling with loading the duration in a React component so following #AlexioVay's solution, here is an answer if you're using React:
This assumes you are using a ref for your audio component class which you will need to target the audio elements for your play/pause handler(s).
<audio /> element:
<audio ref={audio => { this.audio = audio }} src={this.props.src} preload="auto" />
Then in your componentDidMount():
componentDidMount() {
const audio = this.audio
audio.onloadedmetadata = () => {
console.log(audio.duration)
this.setState({
duration: this.formatTime(audio.duration.toFixed(0))
})
}
}
And finally the formatTime() function:
formatTime(seconds) {
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
const s = seconds % 60
return [h, m > 9 ? m : h ? '0' + m : m || '0', s > 9 ? s : '0' + s]
.filter(a => a)
.join(':')
}
With this, the duration in h:mm:ss format will display as soon as the audio src data is loaded. Sweetness.
I used "canplaythrough" event to get the track duration. I have a case where I have two players, and I want to stop the second player 2 seconds before the first one is complete.
$('#' + _currentPlayerID).on("canplaythrough", function (e) {
var seconds = e.currentTarget.duration;
var trackmills = seconds * 1000;
var subTimeout = trackmills - 2000; //2 seconds before end
//console.log('seconds ' + seconds);
//console.log('trackmills ' + trackmills);
//console.log('subTimeout ' + subTimeout);
//Stop playing before the end of thet track
//clear this event in the Pause Event
_player2TimeoutEvent = setTimeout(function () { pausePlayer2(); }, subTimeout);
});
Simply use audioElement.duration
To obtain the end of reading it is necessary that 'loop = false' with the event 'onended'. If loop = true, onended does not work;)
To make a playlist, you have to set loop = false to use the 'onended' event in order to play the next song.
for the duration if your script is done correctly you can recover the duration anywhere. If the value of 'duration' is NaN the file is not found by the browser. If the value is 'Infinity' 'INF' it is a streaming, in this case, adds 1 mn compared to the reading time 'currentime'.
For * I.E it's crap. Currenttime may be greater than duration, in which case you do:
var duration = (this.currentime> this.duration)? this.currenttime: this.duration;
That's (O_ °)
it's better to use the event like this ...
ObjectAudio.onprogress =function(){
if(this.buffered.length){
var itimeend = (this.buffered.length>1)? this.buffered.end(this.buffered.length-1):this.buffered.end(0);
....
Your code here.......
}
}
I use videojs as player and I want if I restart the video for the second time to start reading from the last position before leaving it the last time
avant quitter le player je stocke la position
whereYouAt = player.currentTime();
var currentTime = localStorage.currentTime;
localStorage.currentTime = whereYouAt;
to restart it
playFilmVideo=function()
{
if (player.readyState() < 1) {
player.one("loadedmetadata", onLoadedMetadata);
}
else {
// metadata already loaded
onLoadedMetadata();
}
}
function onLoadedMetadata() {
var weAreAt = localStorage.currentTime;
var dri = weAreAt.split(".");
var LastTime= dri[0];
player.currentTime(LastTime);
}
when I launch the application for the first time the player starts reading from the position last.but when I leave the player without leaving the application and relaunch it again it starts reading from the beginning of the video
knowing that localStorage.currentTime is still recovering the last position
you can set/seek current time of video on load using following snippet.
myPlayer.currentTime(120); // 2 minutes into the video
ref - docs
My solution is to remove the fuction readyState() and it worked well:
player.on('loadedmetadata', function(msg) {
player.play();
onLoadedMetadata();
});
function onLoadedMetadata() {
var weAreAt = localStorage.currentTime;
var dri = weAreAt.split(".");
var LastTime= dri[0];
player.currentTime(LastTime);
}
I have a playlist of videos, with a list of each video in a sidebar. When I click on the name of the video I want to load in the sidebar, the player switches the current video to the one I just clicked.
Some videos have sections, and those sections need to start playing at a certain time in the video. For example, I have a video with 2 sections, Section 1 starts at 0:00, but when I click on "Section 2" in the sidebar, the video should start playing at 1:30 seconds into the video.
Now I got this working with the following code, but the poster image is still playing over the video when I click on Section 2 which should start playing in the middle of the video. How can I get rid of the poster image when starting a video with currentTime offset?
(function($) {
var current, gototime;
var istime = false;
// video.js object
var $player = videojs('ppi-video');
$('a').on('click', function(e) {
e.preventDefault();
var attr = $(this).attr('data-video');
var time = $(this).attr('data-time');
if(typeof time !== 'undefined' && time !== false) {
istime = true;
gototime = time;
}else {
istime = false;
gototime = undefined;
}
// If link has data-video attribute... continue
if(typeof attr !== 'undefined' && attr !== false) {
if( current == attr ) return;
var image_path = "images/screens/";
var content_path = "source/";
// Wait till player is ready
$player.ready(function() {
// Hide the player?
$("#ppi-video_html5_api").fadeOut(400, function() {
// Change poster
$("#ppi-video_html5_api").attr('poster', image_path + attr + ".jpg");
$player.poster(image_path + attr + ".jpg");
});
$player.src([
{ type: "video/mp4", src: content_path + attr + ".mp4" },
{ type: "video/webm", src: content_path + attr + ".webm" },
]);
// Set the currently playing variable
current = attr;
$("#ppi-video_html5_api").fadeIn();
});
}
});
function updateVideo() {
if( istime ) {
$player.currentTime(gototime);
$player.ready(function() {
/**
* Trying to get rid of poster here, but not working
*/
$("#ppi-video_html5_api").attr('poster', '');
$player.poster('');
$player.play();
});
}else {
$player.currentTime(0);
}
}
// update video when metadata has loaded
$player.on('loadedmetadata', updateVideo);
})(jQuery);
I found myself in the same situation where i needed to programmatically hide the poster image. (i wanted the posterimage to hide on drag of a custom scrubbar)
I found two ways that might help someone else who is in the same situation (i know this is an old post, but i came across it looking for an answer).
First and most simply you can hide the poster image using css:
.vjs-poster.vjs-poster.vjs-poster {
display: none;
}
// specificity bumped for default css, your mileage may vary.
However because i wanted this to be done on drag event i figured i might as well just use js:
player.posterImage.hide();
It looks like on Chrome and Firefox, setting currentTime() needs to be delayed a bit. What I did was call play() and then pause() to remove the poster before the video starts. Then I set a timeout of 200 milliseconds which has a callback which then calls currentTime().
Kind of a makeshift workaround but it is working nicely.
Because IOS prevents auto-loading of video it is necessary to add a 'poster' image to indicate a play button (in this case).
However I also want to display a loading image for slow connections by swapping the poster image for a loading image when loading has started.
The problem is on normal connections the play button shows for a split second before the loading image.
So how can I show the play poster image for when it is detected that no loading is going to take place until the play button is pressed.
if ( yourVideoElement.readyState === HAVE_ENOUGH_DATA ) {
// it's loaded
}
https://developer.mozilla.org/en/DOM/HTMLMediaElement
UPDATE
Or you could use jQuery:
var videoDuration = $html5Video.prop('duration');
var updateProgressBar = function(){
if ($html5Video.prop('readyState')) {
var buffered = $html5Video.prop("buffered").end(0);
var percent = 100 * buffered / videoDuration;
//Your code here
//If finished buffering buffering quit calling it
if (buffered >= videoDuration) {
clearInterval(this.watchBuffer);
}
}
};
var watchBuffer = setInterval(updateProgressBar, 500);
You have to check two things, first the networkState and the readyState additionally you have to make sure, that either the preload attribute has a value other than 'none' or you are using an autoplay attribute. In this case you can write the following code (better to wait untill window.onload):
$(window).on('load', function(){
var myVideo = $('video');
if(myVideo.prop('readyState') < 1 && myVideo.prop('networkState') != 2){
//no automatically loading code
myVideo.prop('poster', 'noloading.jpg');
}
});
This is not tested, readyState == 0 means that there is no data and networkState 2 means it does not try to load.
With a timeout:
$(window).on('load', function(){
var myVideo = $('video');
if(myVideo.prop('readyState') < 1 && myVideo.prop('networkState') != 2){
//probably no automatically loading code
setTimeout(function(){
if(myVideo.prop('readyState') < 1 && myVideo.prop('networkState') != 2){
//no automatically loading code
myVideo.prop('poster', 'noloading.jpg');
}
}, 1000);
}
});
Since Firefox doesn't allow me to use an .mp4 file in the <video>-tag, I have to use the Flash-fallback on my VideoJS player.
For Chrome, Safari and IE, I can configure my VideoJS player with javascript to do pretty much anything. For example I like to loop it 5 times, hide the controls and mute the video. No issue there for the HTML5 version:
// Initialize the video with some settings
videojs(videoID, {
"controls": false,
"autoplay": false,
"preload": "auto",
});
var myVideo = videojs(videoID);
// Set the counter
var loop_count = 1;
// Function to loop the video exaclty 5 times
var loopInstagramVideo = function() {
if (loop_count <= 5) {
myVideo.play();
loop_count++;
} else {
loop_count = 1;
}
};
// Function to manipulatie the playing video (mute, no controls,...)
var setVideoOptions = function() {
myVideo.muted(1);
myVideo.controls(0);
};
// Set functions on the video
myVideo.on("play", setVideoOptions);
myVideo.on("ended", loopInstagramVideo);
So I would like to do the same for the Flash version.
The code above is generating an error on the videojs-call with the error:
TypeError: The element or ID supplied is not valid. (videojs)
Any thoughts on how to tackle this issue?
While this isn't an answer for your "looping" question, I just myself discovered that after calling videojs() on an element, the ID changes. Whether it's the ID of the element changes or the focus of the videojs call changes I don't know. The error pertaining to the ID not being valid is being caused by your first and second videojs() calls.
I would change this:
videojs(videoID, {
"controls": false,
"autoplay": false,
"preload": "auto",
});
var myVideo = videojs(videoID);
To this:
var myVideo = videojs(videoID);
myVideo.controls = false;
myVideo.autoplay = false;
myVideo.preload = "auto";
OR put those properties in the video tag itself.