I'm a javascript novice building a simple page to practice using HTML5 video. It's basically a full-page looping video and a javascript timer that tracks and updates time on page. (Inspired, of course, by Infinite Drunk Ron Swanson). Here is a live version of the page. Be careful: both links play sound.
I'd like the script to select one of seven videos to play on page load. The code should choose a random integer between 1 and 7, generate strings to the appropriate files in a "media/" directory, and replace the video <source> tag's src attribute with the new string.
This all seems to be working correctly: there are no errors in the console, locally or as uploaded to github-pages, and when I inspect the <video> element in the Chrome developer toolbar, I can see that the code is replacing the src strings correctly. But the page doesn't seem to load different videos! This is extra-aggravating, as it was working correctly a few commits ago. I've gone back through the history to see what's changed, but I can't find anything.
I come from Python, and I suspect there's something I just don't get yet, but I don't know enough to figure out what!
UPDATE: I have solved this problem with the help of this question, by using createElement and appendChild to insert the source tags instead of changing the attributes of each element in the NodeList returned by querySelectorAll. Here is the relevant new code:
function add_source(element, src, type) {
var source = document.createElement("source");
source.src = src;
source.type = type;
element.appendChild(source);
}
function main() {
var video_no = random_int(1,7);
var video_string_mpeg = "http://ecmendenhall.github.com/Infinite-Charleston/media/Trudy" + video_no + ".mp4";
var video_string_webm = "http://ecmendenhall.github.com/Infinite-Charleston/media/Trudy" + video_no + ".webm";
var videoplayer = document.querySelector(".videoplayer");
add_source(videoplayer, video_string_mpeg, "video/mp4");
add_source(videoplayer, video_string_mpeg, "video/webm");
get_seconds();
}
This is great, but I don't completely understand why it worked. Explanations are still welcome!
Here's the javascript:
start = new Date();
start_time = start.getTime();
function get_seconds() {
var now = new Date();
var time_now = now.getTime();
var time_diff = time_now - start_time;
var seconds = time_diff/1000;
var timer_string = Math.round(seconds);
document.getElementById("timer").innerHTML = timer_string;
window.setTimeout("get_seconds();", 1000);
}
function random_int(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min; }
function main() {
var video_no = random_int(1,7);
var video_string_mpeg = "http://ecmendenhall.github.com/Infinite-Charleston/media/Trudy" + video_no + ".mp4";
var video_string_webm = "http://ecmendenhall.github.com/Infinite-Charleston/media/Trudy" + video_no + ".webm";
var sources = document.querySelectorAll(".videoplayer source");
sources.item(0).src = video_string_mpeg;
sources.item(1).src = video_string_webm;
get_seconds();
}
And the HTML:
<html>
<link rel="stylesheet" href="charleston.css">
<script src="charleston.js" type="text/javascript">
</script>
<body onload="main();">
<video class="videoplayer" loop autoplay>
<source src="http://ecmendenhall.github.com/Infinite-Charleston/media/Trudy1.mp4" type="video/mp4" />
<source src="http://ecmendenhall.github.com/Infinite-Charleston/media/Trudy1.webm" type="video/webm" />
Christ on a cracker, Trudy! Update your web browser!
<audio loop autoplay>
<source src="http://ecmendenhall.github.com/Infinite-Charleston/media/charleston.mp3" type="audio/mpeg" />
<source src="http://ecmendenhall.github.com/Infinite-Charleston/media/charleston.ogg" type="audio/ogg" />
</audio>
</video>
<div class="timer_box">
<p>Hell's bells, Trudy! You've been dancing for <a id="timer">0</a> seconds.
Tweet
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script></p>
</div>
</body>
</html>
I don't think the CSS or media files are the problem, but you can see them in the project repository if you'd like. A live version is available here. (NB: It plays sound). As you can see, it does everything right except selecting different videos.
I believe you can also call .load() on your video object to reload the <source>'s defined in the corresponding video object.
"When your listener function is triggered, it should change the media’s src property, then call the load method to load the new media and the play method to play it, as shown in Listing 3-4." - Apple
Related
I'm trying to dynamically change the video source with the onclick event.
Here's the HTML:
<video autoplay loop poster="1.jpg" id="bg">
<source src="/static/media/1.webm" type="video/webm">
<source src="/static/media/1.mp4" type="video/mp4">
</video>
In onclick I generate a random link, then I change the source of the <video> element:
window.onclick = function() {
var randint = Math.floor(Math.random() * 10) + 1;
var srcmp4 = "http://127.0.0.1:8888/static/media/" + randint.toString() + ".mp4";
document.getElementById('bg').src=srcmp4;
};
If I try to access the source element the same way, by assigning it an id:
<source id="webmid" src="/static/media/1.webm" type="video/webm">
I get null.
I want to dynamically change both .jpg, .webm, .mp4 links. How do I access and assign multiple source elements inside video tag via JS?
The video tag is missing width, height, and controls attributes. Try this:
<video width="320" height="240" id="bg" autoplay loop controls>
<source src="/static/media/1.webm" type="video/webm">
<source src="/static/media/1.mp4" type="video/mp4">
</video>
Additionally here is the new js:
window.onclick = function() {
var randint = Math.floor(Math.random() * 10) + 1;
var srcmp4 = "http://127.0.0.1:8888/static/media/" + randint.toString() + ".mp4";
var video = document.getElementById('bg');
var source = document.getElementByTagName('source');
source = srcmp4;
video.load();
};
After changing that, the source seems to change according to this test.
So i did some reading. First i tried deleting children of video, creating new source elements and appending to video, everything worked in page source, but view didn't render any changes.
So i followed .canPlayType route to determine if client supports .webm, and if not -- assigning mp4 src directly to video element.
Also added small fix so random generated numbers won't repeat. Random video loads when page is loaded and on click events afterwards. Here is the code, it's a mess:
<video autoplay loop poster="" id="bg">
</video>
<script type="text/javascript">
function randlink(a,b){
// generate random int in range of a,b. return str
var c = Math.floor(Math.random()*b)+a;
return "/static/media/"+c.toString();
}
//assign video new random src and poster on page load
window.onload = function(){
var bgvid = document.getElementById('bg');
var randbase = randlink(1,10);
bgvid.poster = randbase + ".jpg";
//check if webm, else mp4 src
if(bgvid.canPlayType('video/webm; codecs="vp8, vorbis"') != ""){
bgvid.src = randbase+".webm";
} else { bgvid.src = randbase+".mp4"}
//change src and poster on click
window.onclick = function() {
var randbase2 = randlink(1,10);
//check if unique
do{
randbase2 = randlink(1,10)
} while (randbase2 == randbase);
randbase = randbase2;
bgvid.poster = randbase2 + ".jpg";
var webmstr = ".webm";
//check if previous was webm, else mp4
if(bgvid.src.indexOf(webmstr) > 0){
bgvid.src = randbase2 +".webm";
}else{
bgvid.src = randbase2 +".mp4";
}}
};
I've already answered this before here: Can I use javascript to dynamically change a video's source?
You have to give the source tags different ids and then add another document.getElementById("source").src = "ANOTHER_SOURCE"; after the first one
I've been googling this for the last couple weeks and have read several things on here that are close to what I'm trying to do but since I'm not too good with javascript, I'm having a hard time figuring out how to implement them.
I have an audio tag that will play a different audio file based on the day of the month. So when you click play it should play the file for that particular day using the audio player.
Here's my audio tag:
<audio preload="metadata" controls="controls" src="" id="todays" autoplay="none"></audio>
Here is my .js:
function todaywm() {
var a, dayofmonth
a = new Date()
dayofmonth = a.getDate()
document.getElementById('todays').src = radiolinks[dayofmonth]
}
var radiolinks = new Array(31)
radiolinks[1] = "audio/day01.mp3"
radiolinks[2] = "audio/day02.mp3"
radiolinks[3] = "audio/day03.mp3"
radiolinks[4] = "audio/day04.mp3"
radiolinks[5] = "audio/day05.mp3"
radiolinks[6] = "audio/day06.mp3"
radiolinks[7] = "audio/day07.mp3"
radiolinks[8] = "audio/day08.mp3"
radiolinks[9] = "audio/day09.mp3"
radiolinks[10] = "audio/day10.mp3"
radiolinks[11] = "audio/day11.mp3"
radiolinks[12] = "audio/day12.mp3"
radiolinks[13] = "audio/day13.mp3"
radiolinks[14] = "audio/day14.mp3"
radiolinks[15] = "audio/day15.mp3"
radiolinks[16] = "audio/day16.mp3"
radiolinks[17] = "audio/day17.mp3"
radiolinks[18] = "audio/day18.mp3"
radiolinks[19] = "audio/day19.mp3"
radiolinks[20] = "audio/day20.mp3"
radiolinks[21] = "audio/day21.mp3"
radiolinks[22] = "audio/day22.mp3"
radiolinks[23] = "audio/day23.mp3"
radiolinks[24] = "audio/day24.mp3"
radiolinks[25] = "audio/day25.mp3"
radiolinks[26] = "audio/day26.mp3"
radiolinks[27] = "audio/day27.mp3"
radiolinks[28] = "audio/day28.mp3"
radiolinks[29] = "audio/day29.mp3"
radiolinks[30] = "audio/day30.mp3"
radiolinks[31] = "audio/day31.mp3"
But I'm just not able to get it to work. I'm still learning javascript.
Update v.2
Second version of audio player will accept a properly formed Array Literal and plays the coresponding position in relation to the day of the month. I have included a form that will accept relevant data to generate an array literal for you as well.
dailyAudio Player
Playlist Array Generator
If you have all 31 files already, you don't need to use an array, it's easier to manipulate the 2 digits of your path to the files. Make sure that your webpage and mp3 files are in the correct folder (directory) of your server.
Example:
Webpage location: http://www.domain.com/path/to/index.html
Song day 28 location: http://www.domain.com/path/to/audio/day28.mp3
Song day 1 location: http://www.domain.com/path/to/audio/day01.mp3
Here's a working demo: http://plnkr.co/edit/vzvOD0XIi61qfVAASWub?p=preview
In order to run it:
Fork it.
Go to the file dailySong.js
Comment line 13 (Place "//" at the beginning of line 13)
Uncomment line 17 (Delete "//" at the beginning of line 17)
Line 17 can only play on day 18, 19, & 20 of the month.
The snippet below doesn't work because it's sandboxed too strictly. So just go to http://plnkr.co/edit/vzvOD0XIi61qfVAASWub?p=preview and follow the directions if you want to test it.
function nowPlaying() {
var player = document.getElementById('dailySong');
var obj, dayOfMonth, X;
obj = new Date();
dayOfMonth = obj.getDate();
X = pad(dayOfMonth, 2); //:::::............. This is to ensure that numbers under 10 get that extra 0 padding.
player.src = "audio/day" + X + ".mp3"; //:::::........This concats X to a string that will be assigned to src
player.load(); //:::::.................When changing src, you must .load() the player before you can play.
}
// This utility function is to pad numbers less than 10
// https://stackoverflow.com/a/30387967/2813224
function pad(num, len) {
return '0'.repeat(len - num.toString().length) + num;
}
//When the page has completely loaded, start nowPlaying function.
window.onload = nowPlaying;
h1 {
font-size: 2rem;
color: red;
}
<audio preload="metadata" controls="controls" src="" id="dailySong" autoplay="none"></audio>
<h1>Do not expect this demo to work, snippets are sandboxed too strictly, follow the directions to test the demo at: <br/>http://plnkr.co/edit/vzvOD0XIi61qfVAASWub?p=preview</h1>
Did you put the ';' on end each line?
The script looks fine (; is not compulsory at the end of each statement, but highly recommended), so you may have a problem with the location of the mp3s. Are they in a folder "audio" relative to the script's location?
#zer00ne here's my code for the audio player. I'm using the Foundation Framework:
<div id="todayModal" class="reveal-modal tiny" data-reveal aria-labelledby="todays-broadcast" aria-hidden="true" role="dialog">
<script type="text/javascript" src="js/today.js"></script>
<h3 id="todays-broadcast" class="text-center">Today's Broadcast</h3>
<div class="audio-player"><audio preload="auto" controls="controls" id="todays" src=""></audio></div>
<a class="close-reveal-modal" aria-label="Close">×</a>
</div>
I have numerous MP3 files in a folder.
When I enter a page, I want a song from that playlist to play. After this song is over, I want a new one to play (not the same one) and so on.
<script type="text/javascript">
var song = ["../audio/1.mp3" , "../audio/2.mp3", "../audio/3.mp3", "../audio/4.mp3", "../audio/5.mp3" ];
var soundFile = song[Math.floor(Math.random()*song.length)];
document.write("<embed id='sound' src='" + soundFile + "' loop='true' type='audio/mpeg' controller='false' height='0' width='0'>");
</script>
What's wrong, because it always plays the same song.
Use an already defined embedded element and set the src attribute.
<embed id='sound' loop='true' type='audio/mpeg' controller='false' height='0' width='0'>
<script type="text/javascript">
var songs = ["../audio/1.mp3" , "../audio/2.mp3", "../audio/3.mp3", "../audio/4.mp3", "../audio/5.mp3" ];
var remainingSongs = songs;
var el = document.getElementById("sound");
var pickSong = function(){
// No songs left?
if(!remainingSongs.length) {
remainingSongs = songs;
}
// Pick song
var index = Math.floor(Math.random() * (remainingSongs.length - 1));
var soundFile = remainingSongs[index];
// Remove song from array
remainingSongs.splice(index, 1);
el.setAttribute("src", soundFile);
}
pickSong();
el.addEventListener("ended", pickSong, false);
</script>
You have an off-by-one error. I think it should be:
var soundFile = song[(Math.floor((Math.random())*(song.length-1)))];
But otherwise this should work. Could be a combination of the erroneous code and weird browser caching?
Also, check the web server you are using to serve the files and clear its cache.
Plus make sure your audio files are actually different.
Also, random chance may be tricking you. Give it a few goes.
This works for me in Chrome Canary on OSX:
<!DOCTYPE html>
<html>
<head>
<script>
var song = ['./foo.m4a', 'bar.m4a', 'bam.m4a'];
var soundFile = song[(Math.floor((Math.random())*(song.length-1)))];
document.write('<embed id="sound" src="' + soundFile + '" loop="true" type="audio/mpeg" controller="false" height="0" width="0">');
</script>
</head>
<body></body>
</html>
The problem is the loop attribute for the embed element doesn't know about your array of audio sources. It's just going to loop over the same source file.
Luckily, there is a pretty easy solution. The following will create an Audio element, with the Audio constructor, and continue to set a "random" audio source from the song array when the previous is done playing. We can do this by listening for the ended event.
<script type="text/javascript">
var song = ["../audio/1.mp3" , "../audio/2.mp3", "../audio/3.mp3", "../audio/4.mp3", "../audio/5.mp3" ];
var audio = new Audio();
var setSound = function() {
var soundFile = escape(song[Math.floor(Math.random()*song.length)]);
audio.setAttribute('src', soundFile);
audio.play();
};
// set initial source
setSound();
// set a new source, when current sound is done playing
audio.addEventListener('ended', setSound, false);
</script>
I have 15 videos I need to show. Instead of creating 15 pages with each html5 video embed targeting a different video, I rather have just one page and via the URL tell the player what to load. This way I can just have 15 custom links but just one player and one html page. Need this to be supported by all browsers, iOS and Android.
Example:
www.mysite.com/videoplayer.html?v=mymovie
www.mysite.com/videoplayer.html?v=mymovie&t=13.6 - this should jump to the player playhead to a point in time.
videoplayer.html
<script>
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
alert('Query Variable ' + variable + ' not found');
}
var videoFile = getQueryVariable("v");
var videoElement = document.getElementById("mp4source");
videoElement.setAttribute("source", videoFile + ".mp4");
</script>
<video id="mp4" controls="controls">
<source id="mp4source" src="../videos/jude.mp4" type="video/mp4" />
</video>
Main.html
<div id="menubar1">
Play Movie
Chapter 2
</div>
I'm a beginner to javascript, please be specific in your answers.
Thanks.
IMHO DOM Manipulation on the main page would be a better solution, but here it is, at least for modern browsers, from your example code.
I changed your getQueryVariable in order to be able to use it as a
boolean.
To change the current playback time, you will have to wait for the
video's metadata to be loaded, then you can set the currentTime
property (in seconds).
In order to comply the "all browsers" support you will have
to transcode your videos to ogg vorbis format, then add a source pointing to
this video file. This will do for major modern browsers.
For older browsers, you will have to add a fallback (e.g. flash player or java applet).
For the "jumping playhead" in ios, you have some tricks to do : look at this question , personally I used this code which seems to work on my ipad ios 8. Note that it will lack of autoplay if you decide to add it in the video tag.
Now, you can't get video for all browsers (e.g text-based browsers).
Live Example
Play Movie Chapter 2
Commented videoplayer.html
<video id="video" controls="controls">
<source id="mp4source" src="" type="video/mp4" />
<source id="oggSource" src="" type="video/ogg" />
</video>
<script>
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
//returning false will help knowing if that variable exists
return false;
}
function loadVideo() {
var videoFile = getQueryVariable("v");
//if v is not set
if (!videoFile) {
alert('please choose a video file, \n maybe you came here by accident?');
//no need to go further
return;
}
//Select the sources for the mp4 and the ogg version
var mp4source = document.getElementById("mp4source");
mp4source.setAttribute("src", videoFile + ".mp4");
var oggSource = document.getElementById("oggSource");
oggSource.setAttribute("src", videoFile + ".ogv");
//if t is set
if (getQueryVariable("t")) {
//userAgent can be overridden but it may be the best way to detect ios devices
var iOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/) !== null;
if (iOS) {
iOSLoadSeek();
} else {
//wait for the video meta data to be loaded
document.getElementById('video').addEventListener('loadedmetadata', function() {
//then change the time position
this.currentTime = getQueryVariable("t");
})
}
}
}
//ios load seek workaround, edited from https://gist.github.com/millermedeiros/891886
function iOSLoadSeek() {
var vid = document.getElementById('video');
if (vid.readyState !== 4) { //HAVE_ENOUGH_DATA
vid.addEventListener('canplaythrough', iosCanPlay, false);
vid.addEventListener('load', iosCanPlay, false); //add load event as well to avoid errors, sometimes 'canplaythrough' won't dispatch.
vid.addEventListener('play', iosCanPlay, false); //Actually play event seems to be faster
vid.play();
setTimeout(function() {
vid.pause(); //block play so it buffers before playing
}, 10); //it needs to be after a delay otherwise it doesn't work properly.
}
}
//called when one of the three events fires
function iosCanPlay() {
//remove all the event listeners
this.removeEventListener('canplaythrough', iosCanPlay, false);
this.removeEventListener('load', iosCanPlay, false);
this.removeEventListener('play', iosCanPlay, false);
//finally seek the desired position
this.currentTime = getQueryVariable("t");
this.play();
}
//When the page is loaded, execute
window.onload = loadVideo();
</script>
In testing out the HTML5 track element, the cue comes up null. The TextTrackList and track element appear to load. Also, I am aware that VTT files aren't accessible locally, and I'm testing on a server. Can anyone help out? Thanks in advance.
This is my Javascript:
var audioElement = document.querySelector("audio");
var textTracks = audioElement.textTracks;
var textTrack = textTracks[0];
var ques = textTrack.cues;
var que = ques[0];
console.log(que);
Here's the HTML:
<audio src="Audio Files/Q_firefox.ogg" controls>
<track src="cues.vtt"></track>
</audio>
The cue value changes as the track plays so you need to listen for the value to change and run your function each time the text changes. Try this example based on your question:
var audioElement = document.querySelector("audio");
var textTrack = audioElement.textTracks[0];
// When cue value changes run your code
textTrack.oncuechange = function(e) {
var cue = this.activeCues[0];
if(cue){
console.log(cue.text);
}
}
<audio src="http://html5-demos.appspot.com/static/video/track/Google_Developer_Stories.webm" controls>
<track src="http://html5-demos.appspot.com/static/video/track/video-subtitles-en.vtt" default>
</audio>
Also note no need to close HTML track tag.