My website plays background music with autoplay. I made it use my custom controls for play and pause. Now, I'd like to set the initial state according to what is going on. If the music is about to play for real, it should show pause icon, otherwise (e.g. on mobile) play icon.
I would use audio.paused boolean value, but it's always false before the audio is loaded.
I would use audio.autoplay value, but it's always true for me, even on devices that don't support it.
Is there any clean way to know whether the audio will be played? I would like to keep it in sync with autoplay attribute, so if I decided to remove it, the state should always show play icon in the beginning.
Just playing or even buffering songs isn't especially fair, when there is the slightest chance people can be on the site for other reasons, like for example to check for updates, to share the link. people can be on the page with a mobile network, with limited bandwidth and downloads of those sizes shouldn't ever start sneaky behind their back.
edit: a few additional references
Here is an overview over the reasons not to have music on autoplay
And contrary, a website I personally like a lot with a great use of background music on autoplay
But if you are already building your own player and want that to be a feature of the page, setting that player to autoplay would not only devalue your own work, totally break your design. Instead you could just trust that people who want to hear the music will identify your audio player and use it.
To fully implement your custom player GUI you may want to listen for all audio events on the player element and update your view accordingly. The event you are looking for is "canplaythrough" but you probably want to react to at least most of the other events too. Those events are:
playing
waiting
seeking
seeked
ended
loadedmetadata
loadeddata
canplay
canplaythrough
timeupdate
play
pause
ratechange
volumechange
suspend
emptied
stalled
You currently may do something along the lines of
view.showPlayButton();
player.play();
but that breaks as soon as you at some point want to toggle your player in some other way or something else happens, like it gets stalled, so better listen to the event and update your GUI in one place, and control the playback (like start / stop the player) in another.
Related
I am attaching a screenshot for reference to get more elaborated idea about the problem am facing.. Kind of hell.
I am well aware about autoplay policy and have gone through possible approches which involves user interaction. Also, am not a fond of displaying any screen or button to user to make him click, never, i do not want that.
I am developing a wordpress plugin and having microphone feature which can attach to textbpx on any wordpress website, user clicks on it (user interaction) comes in.
I have also read that i need to resume suspended audio context, which am doing and as you can see in screenshot the state of audio context "running" before and after audio elements play mathod.
I am using audio element created using Audio() constructor.
Note: screenshot is a photo of mac system, I am debugging iphone xr using usb cable.
Can somebody help me out or enlighten me on what am doing wrong or there is any technical limitation.
Sigh! after a week lasted hell finally I managed to make things works.
To give an overview of solution let me first describe high level rough idea of work flow which has problem on iOS Sfari.
Problematic workflow
clickHandler --> AudioContext creation --> Playing audio using audio element.
All the audios followed by this workflow miserably failed in 'play' promise.
Solution which worked for me
clickHandler --> Play audio using audio element -> AudioContext creation --> Playing audio using audio element.
The point of interest in a solution which worked for me is you have to play audio using audio element as first line of code in 'clickHandler'. No doubt the promise still fails but subsequent audios does play.
Also, I was creating new audio element for each new audio source to be played, Which was wrong as auto-play policy imposed on 'per-element' basis. So instead of creating new audio element for each source I just create it once on page load (or whatever suits for you as one time creation) and whenever I want to play different audio file I just change the '.src' property/attribute.
So this is how It worked for me. I must mention throughout the week lasted hell, previously answered questions here and their problem specific solutions have been guiding light which gave me new perspective in a hunt for solution. Also the articles on internet, webkit , chrome, apple's documentation on Auto play policy helped a lot.
I have a Netflix account and I have peeked under the hood at its video player running inside Google Chrome. Netflix calls its video player "Cadmium" and the javascript exposes all the functions and event handlers you might expect, such as play, stop, pause, mute, etc. I'm building a little Chrome extension that would enable me to call these Cadmium player function, but the hard part for me is figuring out how to create an instance of the player so I can start calling. The javascript is large, complex, and somewhat obscure. Once I can create an instance of that player, I'm thinking that making calls into the functions will be easy.
Here is a relevant chunk of js:
muteOn: function() {
this.savedVolume = this.getVolume(),
this.updateVolumeDisplay(0),
this.scrubber.updatePercent(0),
this.muted = !0,
this.videoPlayer.setMuted(this.muted)
}
In Chrome dev tools I can set a breakpoint inside that block, and execution hits the breakpoint when I click the Mute button on the netflix video player. The Netflix js is (unsurprisingly) heavily obfuscated via method renaming. I tried stepping through the code in the debugger and ended down a hundred rabbit holes, never able to find my way to the top of the stack, so that I could make that same call (at top of stack) to simulate the user clicking the mute button. I also tried the approach of programmatically clicking the mute button on the UI player, which would meet my needs equally well, but they have serious defensive mechanisms in there, spinning me like a top.
Since there are over 100K lines of javascript, and I'm uncertain which chunks exactly would be relevant for this post, I would like to suggest that you load Netflix in Chrome, open dev tools, play a movie, and inspect the pause or mute button. Interacting with those video player controls takes you into the maze of javascript which I'm trying to see how I can tap into to control aspects of the player programmatically (just from dev tools is fine for now). Another important thing I need to figure out is how to query the video player to determine the current elapsed time of the playing video.
Any ideas how I can crack this nut? (Thanks in advance!)
Using Chrome, I get playback using HTML 5 video.
Once you get a hold on the <video> tag element, you can use the HTML 5 video API:
Get the <video> element
var video = document.evaluate('//*[#id="70143639"]/video',document).iterateNext()
70143639 is the id of the video, as in https://www.netflix.com/watch/70143639
Remaining time (HH:mm)
document.evaluate('//*[#id="netflix-player"]/div[4]/section[1]/label', document).iterateNext().innerHTML
Elapsed time (seconds)
video.currentTime
Elapsed time updates
video.addEventListener("timeupdate",
function(e) {
console.debug("Seconds elapsed: ", e.timeStamp/1000/60);
}
);
Note that I don't get the same results as with video.currentTime, you may need to use an offset based on the difference. Also it may be something explained in the spec: https://www.w3.org/TR/html5/embedded-content-0.html
Play
video.play();
Pause
video.pause();
Go back and forth in time
Courtesy of rebelliard:
netflix.cadmium.UiEvents.events.resize[1].scope.events.dragend[1].handler(null, {value: 600, pointerEventData: {playing: false}}); where 600 is the number of seconds to seek.
Note that I ran into "Whoops, something went wrong..." using this:
video.currentTime += 60;
Even with pause and play calls. This is what this demo page does, you nay need to read the full spec on seeking.
Mute and get muted status
video.muted = true
Like video.currentTime, this is a writeable property.
I'm looking for a way to create something like a loading animation on a html5 video similar to the Youtube Video display (reference: https://www.youtube.com/watch?v=5vcCBHVyG50)
I tried it with the canplay-Event but I think I misunderstood the real meaning of this event.
My thought of this event was that enough data has been loaded and buffered so that the video can be played again.
But in my case this event just fires once. At the beginning of the video.
Is there any special Event which will be fired when the video is playable or needs to load more data?
Use fontAwesome framework. It has got your animation.
I'm working with HTML5, making an audio player.
I wonder to know how I can add a buffer bar so that the user can see how long the song is loaded.
I've tried using several properties that I saw in some tutorials, but none have worked, and I can not find anything about this specific topic.
This is the audio player that I'm trying to edit.
I wish someone could guide me on how to edit the code to do it, or, otherwise, recommend tutorials, documentation or any information.
There's no guarantee that all of this is going to work on all browsers...
...however, assume, for the rest of this post that:
var audio = new Audio();
audio.src = "//example.com/some-really-long-song.mp3";
"canplay" is the first useful event, in terms of being able to play the song.
It means that part of the song is ready to go, and it's at least enough to be able to hear something, if you hit the play button.
Alternatively "canplaythrough" is the browser's guess at whether you can start playing the song right now, and it will run without stopping (based on amount of data left to download, and how fast the song is currently downloading).
audio.addEventListener("canplay", function () { audio.play(); });
"durationchange" is an event which should fire when a duration changes.
For file-types which don't have metadata, or where the metadata isn't supported, the whole duration might not be available as the file starts loading.
In these cases, audio.duration might be updated by the browser downloading the first bit of the media, and then the last, and making an educated guess at the length of the file, or the duration might come from the browser having to load more and more of the file, and increasing the duration as it goes. Where we are in terms of support, I don't know.
audio.addEventListener("durationchange", function () {
player.time.duration.update(audio.duration);
});
"progress" is an event which fires ~250ms as data is coming in (while downloading the file).
progress tells you that updates have been made to data which is available to seek through right now.
This makes it a good event to use to check your audio.buffered objects, in order to update the "loaded" portion of your progress bar.
audio.addEventListener("progress", function () {
player.progressBar.update(audio.buffered.start(0), audio.buffered.end(0));
});
Then you can use "timeupdate" to deal with playback.
timeupdate will update a few times a second, as the song is playing (as audio.currentTime moves forward).
I'm writing a Chrome extension which controls background music (Pandora, Google Music, etc) in response to Youtube events.
I'm able to detect when the video is started, paused, or stopped by adding an event listener to the player. However, the video switches states from playing to paused when seeking. This causes the background music to respond (unpauses) when the user is skipping in the video.
Is there any way to catch this click-and-drag seek event from within Javascript? A workaround (which might be the correct behavior) is to only unpause background music when the video ends--not just pauses--but I was curious if anybody had tackled something like this before.
Seeking is normally a quite a short event, so make the music only start up again after a few seconds have elapsed after the player is paused.