webhtmlviewer mp3 player javascript

I have designed a web app to play mp3 tracks located on a separate server by placing the webhtmlviewer of the webpage.
This enables me to prevent to user downloading the tracks easily, as the url is invisible.

This works well on pc and mac desktops but android users have the option of downloading the track.

As I will be preventing android users accessing the app I am not too concerned.

However my question is it possible to detect when the track is finished and how much time is remaining?
I guess javascript is the answer…
As I haven’t got the foggiest how to implement a java script in the app I need some pointers and perhaps examples.

Would I place the java script within the open or show event of webhtmlviewer??
I was initially thinking about initiating my own in app count down timer but realise this would become inaccurate if the users network connection was unreliable.

Any thoughts geniuses…

The WebMoviePlayer in modern browsers is based upon HTML5, so you can access the JavaScript currentTime property which tells you the position in the playback.

OK thanks Michel,
how would that be expressed be simply expressed xojo, perhaps you might be able to suggest example I could have a look at?

Read Michel’s post on how to return a value to xojo from javascript with the use of the session hashtagchanged, Thanks Michel :slight_smile:

I have a button which fires:
ExecuteJavaScript( " var vid = document.getElementById(‘HTMLViewer1’);" +_
" if (vid.ended) {" +_
" window.location.replace(’#’+‘track finished’)}")

within the HTMLViewer1 i have an mp3 playing.
when executing the javascript an error message is displayed

Could not execute returned javascript: null is not an object (evaluating ‘vid.ended’)
Source: var vid = document.getElementById(‘HTMLViewer1’); if (vid.ended) { window.location.replace(’#’+‘track finished’)}

seems that I am not providing the correct id for the audio player within HTMLViewer1 or is that not the case??

[quote=219888:@James Moore]ExecuteJavaScript( " var vid = document.getElementById(‘HTMLViewer1’);" +_
" if (vid.ended) {" +_
" window.location.replace(’#’+‘track finished’)}")


JavaScript does not know the Xojo name of a control, but it’s ControlID.

You must indicate that to the JavaScript. I did not test your particular code, but this should work:

[code]ExecuteJavaScript( " var vid = document.getElementById(’" + HTMLViewer1.ControlID + “’);” +_
" if (vid.ended) {" +_
" window.location.replace(’#’+‘track finished’)}")


If I may, you should use track_finished’ instead. URLs should not contain spaces.

[quote=219434:@James Moore]OK thanks Michel,
how would that be expressed be simply expressed xojo, perhaps you might be able to suggest example I could have a look at?[/quote]

Const read_position As String

var vid = document.getElementById("myVideo"); window.location.replace('#'+vid.currentTime+"/"+vid.duration) ;

In a button :

Sub Action() self.ExecuteJavaScript(ReplaceAll(read_position,"myVideo",MoviePlayer1.ControlID+"_video")) End Sub

When the code in the button Action event is executed, the JavaScript reads CurrentTime and duration of the HTML5 player, and places that in the HashTag as for instance :

Seconds are expressed with a fractional part.

You can get that back in the Session.HashTagChanged event, by reading the Session HashTag property. It is a string, so you can split it with “/” and turn each term into a number. The process is described for the where method at https://forum.xojo.com/26509-serious-issues-with-events-not-firing

Note that because this uses a particular property in the Xojo framework (MoviePlayer1.ControlID+"_video"), that method is not sanctionned by Xojo and not guaranteed to work if at some undetermined point in time in the future Xojo changed the said framework. I know Greg will warn against that, but since Xojo does not give access to this property, that is the only simple option available for now.

The proper alternative would be to implement the HTML Audio video in a WebSDK control, but it goes way beyond the scope of this thread.

Finally, you should know that Xojo Web uses a fallback with Flash for older browsers that cannot support HTML audio and video. In such a case, the code I posted will not work. See http://www.w3schools.com/html/html5_video.asp for the minimum versions required for the most popular browsers. You may want to test Session.Browser and Session.BrowserVersion before you call the method, to avoid errors.

Hi Michel feels like it’s nearly there…

I placed your code in the button EXACTLY as so:

ExecuteJavaScript( " var vid = document.getElementById(’" + HTMLViewer1.ControlID + “’);” +_
" if (vid.ended) {" +_
" window.location.replace(’#’+‘track_finished’)}")

I did not get any errors but when the track was finished and I fired the button got no reaction…

Seems we are not talking about the same thing.

My code refers to the WebMoviePlayer. Not HTMLViewer. You should read again the post I made above where I explain that _video is necessary after the controlID.

Also, you cannot do if(vid.ended) in JavaScript, it should be a test of the variable content. Something like


Have you tried to implement the code at W3C in HTML ? Is that why you reference HTMLViewer ? How do you play your music ?

Hi Michel

No problem for clarity I will just re-iterate the main points of my opening comment and add a bit more detail…

Using xojo web 2014 r3.2 I have web page with 2 buttons and an HTMLviewer, I have not used webmovieplayer as this will not play mp3s’.
The HTMLviewer is located off stage so the track url cannot be used to download the mp3.
An array located in app contains all the tracks’ urls which are passed to the HTMLviewer.

As the viewer is off stage the user will not be able to tell whether the track is complete so the javascript required will test the media player and change other control settings by utilising the session.hashtagchanged event.

For the sake of this conversation I have 2 buttons on my xojo webpage one to load the url of the track to ‘HTMLviewer1’

and the second to execute the javascript within ‘HTMLviewer1’.
That script being:
HTMLViewer1.ExecuteJavaScript(“var vid = document.getElementById(‘HTMLView1.ControlID video’);" +
"if (vid.ended = ‘true’) {window.location.replace(’#’+‘track_finished’)}”)

When the track has completed and the second button is pressed I get the following error message:

Could not execute returned javascript: null is not an object (evaluating ‘vid.ended = ‘true’’)
Source: var vid = document.getElementById(‘HTMLView1.ControlID _video’);if (vid.ended = ‘true’) {window.location.replace(’#’+‘track_finished’)}

My apologies if have missed something obvious here…

Thanks for your valued help so far :slight_smile:

[quote=220174:@James Moore]Could not execute returned javascript: null is not an object (evaluating ‘vid.ended = ‘true’’)
Source: var vid = document.getElementById(‘HTMLView1.ControlID _video’);if (vid.ended = ‘true’) {window.location.replace(’#’+‘track_finished’)}


That won’t work. The _video thing was for MoviePlayer. What you want to use is something like findElementByTag(‘audio’) if that is what you have used in the HTMLViewer HTML.

There is another issue here : HTMLViewer is an iFrame, and AFAIK since it has its own DOM there is no way to reach to the containing document URL.

You may want to place your Audio tag inside a WebSDK WebControlWrapper instead. Then you should be able not only to execute your code, but also to use Xojo.Trigger.ServerEvent to send the value back to Xojo.

Or insert the audio tag inside another WebControl, such as a label, so you can access it in the page DOM.

mmm in your opinion Michel, what would be the easiest and quickest option, bearing in mind both are ‘alien’ concepts to me.

There are ways to play mp3 in the page itself using a simple label. I described that trick in https://forum.xojo.com/24734-instant-click-sound-in-web-app

Now that was a while ago and since, I see the Audio tag offers more possibilities.

I will see what I can do.

Here it comes. Instead of using HTMLViewer, let us use a simple label. It can be placed off screen not to be seen. Call it Label1.

In the window, add a constant :

<audio ID="myAudio" controls> <source src="mp3" type="audio/mpeg">Your browser does not support the audio element.</audio>

It is very important that this constant does not have any endOfLIne inside. It would trigger a JavaScript error.

Add a method :

Sub LoadMusic(URL as string) dim myMusic as string = ReplaceAll(musik,"mp3",URL) dim js as string = "document.getElementById('"+Label1.ControlID+"').innerHTML= '" js = js + myMusic +"';" self.ExecuteJavaScript(js) End Sub

In Label1.Open :

Sub Open() LoadMusic("") self.ExecuteJavaScript("music = document.getElementById('myAudio'); ") End Sub

To play music :

[code] LoadMusic(URL of the MP3)
dim js as string
js = js + “var music = document.getElementById(‘myAudio’); music.play() ; alert(music.ended);”


To check ended :

self.ExecuteJavaScript("if (music.ended) {window.location.replace('#'+'track_finished');};")

From what is said at http://www.w3schools.com/tags/tag_audio.asp all current browsers support mp3.

Thanks Michel

I have created a xojo webpage ‘webpage1’

within the webpage I have created a constant ‘kMusic’ as a string:

(this copied and pasted here directly from the text editor in xojo)

I have created on the webpage

  1. playBTN
    on Action event-
    dim js as string
    js = js + “var music = document.getElementById(‘myAudio’); music.play() ; alert(music.ended);”


  1. checkBTN
    on action event-
    self.ExecuteJavaScript(“if (music.ended) {window.location.replace(’#’+‘track_finished’);};”)

  1. Label1
    on open event
    LoadMusic(" ")
    self.ExecuteJavaScript("music = document.getElementById(‘myAudio’); ")

loadMusic(URL as string)
dim myMusic as string = ReplaceAll(kMusic,“mp3”,URL)
dim js as string = “document.getElementById(’”+Label1.ControlID+"’).innerHTML= '"
js = js + myMusic +" ';"

on the Label1 open event I have tested it by-
removing loadMusic and providing the URL string both create runtime errors

I have also tested it by removing the constant (kMusic) and providing the string directly to the loadMusic Method

I am not able to locate where the problem is as the runtime error doesn’t really help

runtime error produced below:

Could not execute returned javascript: undefined is not an object (evaluating ‘Xojo.controls[‘J833zAcq’].setStyle’)
Source: var langdir = document.getElementsByTagName(‘body’)[0]; if(langdir) { langdir.removeAttribute(‘dir’); }
var langdir = document.getElementsByTagName(‘body’)[0]; if(langdir) { langdir.setAttribute(‘lang’,‘en’); }
document.getElementById(‘J833zAcq’).style.minWidth = ‘600px’;
document.getElementById(‘J833zAcq’).style.minHeight = ‘400px’;
document.getElementById(‘J833zAcq’).style.minWidth = ‘600px’;
document.getElementById(‘J833zAcq’).style.minHeight = ‘400px’;
Xojo.controls[‘J833zAcq’].object().style.zIndex = ‘1’;
music = document.getElementById(‘myAudio’);
new textlabel(‘WVH4hUPt’,[‘Open’,‘TextChanged’]);
new button(‘ZY9foXy6’,[‘Action’]);
new button(‘OCrXN4hD’,[‘Action’]);
new frameworkObject(‘J833zAcq’,[‘Close’,‘Open’,‘Shown’]);
document.title = “Untitled”;

After changing the label1 event ‘open’ event to ‘shown’ event
label1 shows the track ‘loading’ but never completes.

When the playBTN is fired this runtime error is created

Could not execute returned javascript: null is not an object (evaluating ‘music.play’)
Source: document.getElementById(‘K9URPzLA’).innerHTML= ’ ';
var music = document.getElementById(‘myAudio’); music.play() ; alert(music.ended);

If you are on Mac, make sure you set the Keyboard preferences Text tab option “Use smart quotes and dashes” unchecked.

The constant is polluted by curly quotes. You may have copied it to TextEdit or Word before pasting it into Xojo. Make sure every double quote is a double quote.

Hi Michel
Ok GOT IT WORKING!!! Thank-you for your time and effort, It really is most appreciated.

I made Just one amendment to the ‘check ended’ code…you were probably inferring that the variable had to be set up again as in the play code but just to confirm the entire code for the check button should read:

To check ended :
dim js as string
js = js + “var music = document.getElementById(”“myAudio”"); alert(music.ended);"