YouTube Javascript Iframe API

Do I have access to the YouTube Iframe API from the web interface?
I tried playing with JS.eval, but if that is supposed to work, I don't seem to understand how.

https://developers.google.com/youtube/iframe_api_reference#Examples

I can only use the web interface, so I can't create functions and libraries to add to the game.

If there is a way to get it to work from the Web Interface, it looks like I can dynamically create arrays of video clips to play, I will be able to listen for various triggers based on video state, ready, finished playing, etc. and that will add some extra cool stuff I can do with these videos to work around the limitations of not being able to autoplay* from a web browser.

*very similar situation with other browsers

"Warning: To prevent unsolicited downloads over cellular networks at the user’s expense, embedded media cannot be played automatically in Safari on iOS — the user always initiates playback."

Do I have access to the YouTube Iframe API from the web interface?

It's a javascript API.
Quest sends HTML and Javascript to the player's browser. There is no restriction on what that javascript can include, because as far as Quest is concerned it's just a string that needs to be sent to the browser. You can do absolutely anything that you could do in a static webpage.

I don't think there should be any problems with using the example code shown on that page in Quest; though it might be neater to have something like this to load the library:

$(function (){
  onYouTubeIframeAPIReady = function () {
    // any code here that you want to run as soon as the API is ready - probably not necessary if you're just loading it ready to run when you add videos to the page later
  };

  // This line loads the API:
  $('<script>', {src:'https://www.youtube.com/iframe_api'}).prependTo('head');
});

I can only use the web interface, so I can't create functions and libraries to add to the game.

You can't create libraries, but that shouldn't matter too much. It just means you'll have to create all your functions one by one if you want to reuse them in another game.
You can create functions fine; the only real limitation of the web version is that you can't replace Quest functions that are defined in the core libraries.

If you mean javascript functions, you can override them just fine. You might not be able to include *.js files, but there's nothing to stop you minifying a javascript file and putting the whole thing in a JS.eval() statement.

My favourite way to make it easier to play with javascript is this UI Initialisation script:

foreach (jsobj, AllRooms()) {
  if (StartsWith (jsobj.name, "JS_")) {
    if (HasString (jsobj, "description")) {
      jstring = jsobj.description
      jstring = Replace(jstring,Chr(60)+"br/"+Chr(62),"\n")
      jstring = Replace(jstring,"«",Chr(60))
      jstring = Replace(jstring,"»",Chr(62))
      JS.eval (jstring)
    }
  }
}

This means that any rooms with "JS_" at the start of their names will have their descriptions treated as javascript and sent to the browser. Just use « and » in place of < and > within the code, to avoid the editor mangling it.


To test, I put this in the game user interface init

$(function (){
  onYouTubeIframeAPIReady = function () {
  }
  ;
  $('<script>', {src:'https://www.youtube.com/iframe_api'}).prependTo('head');
}
);

Error: Error adding script attribute 'inituserinterface' to element 'game': Missing '}'

I don't see anything missing, and I don't understand what I would need to change. Last time I worked with HTML was when it was still 1.0 in 1996. I focused mostly on CISCO network management and CLI work, even though I studied coding, I didn't play with it as much as looking at a report from routers and switches and understanding how to fix the issue.


K.V.

Quest can't directly read Javascript. You have to use JS.eval(), like so:

JS.eval ("$(function (){  onYouTubeIframeAPIReady = function () {  }  ;  $('<script>', {src:'https://www.youtube.com/iframe_api'}).prependTo('head');});")

NOTE: This is untested and assumes that JS code works properly when ran at that point in the game.


I still don't understand what this is supposed to do for me. The more I play with this, the more it feels like I am just throwing code into a black hole.

I am starting to remember why I always disliked programming, too many levels of abstraction that aren't relevant to the language used to produce the results.

I almost need to have an actual conversation with someone about the capabilities of Quest and what I would like to produce so I get understand how to proceed.

And having to reload the website every time there is a problem with the code, or error messages that don't clarify the error, it feels like I keep walking into the same glass wall.

I have no desire to create complex environments for players to interact with, I don't want to write long stretches of story for the player to read.

I just want to load YouTube video clips, images, and perhaps some menus that allow the player to take different paths through the information.

The only two benefits I can see of the API is having flags that can potentially tell Quest when a video is done playing, so some other action can be take based on that trigger. The second being the ability to pass array of video id's into the API and have it play them like a playlist.

The first, I don't think quest can do for me, other than using timers to estimate how long a person would take to watch a video based on clip length and a buffer.

The second I can use String lists and switch case statements and flags and timers and waits or some combination of features to load the videos one by one from a string list using the ShowYoutbe() function. Cumbersome and probably going to bog down the engine if the playlists get to big.

Otherwise, the API isn't overly useful at this point.


The only two benefits I can see of the API is having flags that can potentially tell Quest when a video is done playing, so some other action can be take based on that trigger.

Looks easy enough. The API's state change event is called whenever a video player changes state; with the valid states being: Paused, Buffering, Cued, Playing, and Ended. You could use this to trigger an ASLEvent if you want the javascript to notify Quest that the video has finished.


ok. The problem I am having is getting a player window to show using the API.

All the code I try doesn't bring up a window.

do I need to run any changes to my game's HTML with this?
Seems from looking at how other people are using the API on their sites, they open their code with a

element that creates the space for the Iframe to function in.

Will Quest handle this form me? Or do I need to implement my own CSS and HTML as well as Javascript.

I see here
http://docs.textadventures.co.uk/quest/ui-custom.html

That I can use JS.setCss and perhaps some other JS functions.

and
http://docs.textadventures.co.uk/quest/ui-callback.html

for ASLEvent callbacks.

Right at the moment, I am not getting a window to pop up.

here is the mess of sample code I am playing with

JS.eval ("$(var player; function (){  onYouTubeIframeAPIReady = function () { player = new YT.Player('player', {
      height: '390',
      width: '640',
      videoId: 'M7lc1UVf-VE',
      events: {
        'onReady': onPlayerReady,
        'onStateChange': onPlayerStateChange
      }
    }
    ); }
  function onPlayerReady(event) {
    event.target.playVideo();
  }
    ;  $('<script>', {src:'https://www.youtube.com/iframe_api'}).prependTo('head');});")

following error, I am sure the formatting of my code is troublesome, just a quick copy and paste to test if it works.

Error: Error adding script attribute 'script' to element 'youtubeTest': Missing quote character in ; $('

I have the code in a function called youtubeTest which is called from the videos object with a verb called explore.

This is going to be a learning curve, but hopefully once I get over this hump, I can start building something interesting and fun, at least for me.


K.V.

Your JS.eval() code has to all be on the same line.

If you have those line breaks in the code, Quest will get very confused.


You could also try creating a room and pasting your JS code into the description, to be converted by JS.eval() in your user interface initialisation script, as mrangel suggests here:

My favourite way to make it easier to play with javascript is this UI Initialisation script:

foreach (jsobj, AllRooms()) {
  if (StartsWith (jsobj.name, "JS_")) {
    if (HasString (jsobj, "description")) {
      jstring = jsobj.description
      jstring = Replace(jstring,Chr(60)+"br/"+Chr(62),"\n")
      jstring = Replace(jstring,"«",Chr(60))
      jstring = Replace(jstring,"»",Chr(62))
      JS.eval (jstring)
    }
  }
}

This means that any rooms with "JS_" at the start of their names will have their descriptions treated as javascript and sent to the browser. Just use « and » in place of < and > within the code, to avoid the editor mangling it.


So I can also do this with objects and their descriptions, or just rooms? Say I wanted to use the videos object as the place where I put this code, or any other non-room object
(say things I place on the walls of the room, like little cubes that activate as a sequence of events is completed, like a puzzle. I could use an examine verb to activate a video playback as long as the smartphone is equipped?)

And what about the code format?
Do I need to put the various API functions each in their own JS.eval () call?

And as far as the code, I should store it in a variable?
something like this from here,
http://docs.textadventures.co.uk/quest/ui-javascript3.html
Just put the entire function code I want to call inside some variable so it doesn't upset the Quest compiler?

s = "$('#gamePanesFinished').html('<h2>Game Over</h2>"
s = s + "<p>This game has finished and you are dead!</p>"
s = s + "<img src=\"" + GetFileURL("gravestone.png") + "\" />"
s = s + "');"
JS.eval (s)

So I can also do this with objects and their descriptions, or just rooms?

I used rooms because they have a description field which gives you a big textbox, large enough for putting javascript in.
Note that these are not rooms the player can enter; they are just containers for a large piece of javascript. They exist just so that you can have a large chunk of code in a string without having to paste s = s + " at the start of every line.

Do I need to put the various API functions each in their own JS.eval () call?

There's a chance that could lead to some odd errors; I've not looked into this particular API in much depth, but in most cases you're better putting all the related javascript functions in one script.


I have tried this with « » and < >, the only thing I get is an empty iframe when I use iframe in the room description with < >.
What ever Quest is setup to do to code is like dealing with QuakeC in the early days of Quake modding, when there was no documentation and nothing made sense in the console.
I have this code in the
game start up init

if (StartsWith (jsobj.name, "JS_")) {
  if (HasString (jsobj, "description")) {
    jstring = jsobj.description
    jstring = Replace(jstring,Chr(60) + "br/" + Chr(62), "\n")
    jstring = Replace(jstring,"«", Chr(60))
    jstring = Replace(jstring,"»", Chr(62))
    JS.eval (jstring)
  }
}

Nothing else works, even with the above foreach (){} that evaluates the room description and runs it in a JS.eval(jstring). I have put that code in a function that is called with I look at a room description, and in the game init script. Nothing happens. There is no YouTube iframe player loaded, only this empty box, as below, AND ONLY when I leave the iframe inline in the description as it is here.

this was encouraging for a moment, until I realized that it was just parsing the HTML and nothing was running the Java script.
But, it seems promising. IF I can set this up so that the player can look at different 'rooms' like an Alternate Reality overlay with Google Glasses, that would be cool. But so far, nothing is working.
This is what is now in the JS_functionCallTest look at room description
Just trying to see if I can get anything else to work, and nothing.

msg("fuckme')
JS.eval(){
<iframe>
<div id="player"></div>

    <script>
      // 2. This code loads the IFrame Player API code asynchronously.
      var tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      // 3. This function creates an «iframe» (and YouTube player)
      //    after the API code downloads.
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
          height: '390',
          width: '640',
          videoId: 'M7lc1UVf-VE',
          events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
          }
        });
      }

      // 4. The API will call this function when the video player is ready.
      function onPlayerReady(event) {
        event.target.playVideo();
      }

      // 5. The API calls this function when the player's state changes.
      //    The function indicates that when playing a video (state=1),
      //    the player should play for six seconds and then stop.
      var done = false;
      function onPlayerStateChange(event) {
        if (event.data == YT.PlayerState.PLAYING && !done) {
          setTimeout(stopVideo, 6000);
          done = true;
        }
      }
      function stopVideo() {
        player.stopVideo();
      }
    </script>
</iframe>
}

K.V.

You've got syntax issues, but we'll get to those in a moment.


Are you making a gamebook or a text adventure?


text adventure.

At this point, I am just copying and pasting code from the youtube iframe api site, because I don't have a step through debugger, maybe I can use the chrome inspect element, but that seems cumbersome, and I don't understand what changes need to be made to fit within the confines of the Quest engine.


K.V.

This is how I got it to do something (not 100% sure it's exactly what you want it to do):

STEP 1

Create a room. Name it YouTubeAPI

NOTE

Before we proceed any further, note that any time we put // in Javascript, that comments out the rest of that line of code. When using JS.eval() in Quest, we must feed that function a string parameter (which is exactly 1 line of code long). mrangel has a script to convert JS that is not written inline to a string which Quest can process (which is accomplished by removing the line breaks). Therefore, the first line of code in that Javascript script will comment out the entire script, unless we format it differently.

Where they have: // comment here, I have changed it to /* comment here */. This leaves the notes intact, but only comments out the text between /* and */.

So, back to it:

STEP 2

Click on the "Room" tab, and paste this into the text field (note that I have fixed all the comments as described above):

       /* 2. This code loads the IFrame Player API code asynchronously. */
      var tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      /*3. This function creates an <iframe> (and YouTube player) */
      /*  after the API code downloads. */
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
          height: '390',
          width: '640',
          videoId: 'M7lc1UVf-VE',
          events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
          }
        });
      }

      /* 4. The API will call this function when the video player is ready. */
      function onPlayerReady(event) {
        event.target.playVideo();
      }

      /* 5. The API calls this function when the player's state changes. */
      /*    The function indicates that when playing a video (state=1), */
      /*    the player should play for six seconds and then stop. */
      var done = false;
      function onPlayerStateChange(event) {
        if (event.data == YT.PlayerState.PLAYING && !done) {
          setTimeout(stopVideo, 6000);
          done = true;
        }
      }
      function stopVideo() {
        player.stopVideo();
      }

At this point, it should look something very much like this:

image


STEP 3

Create a new function, and name it SetupYouTubeAPI

Click on "Code View" and paste this in:

msg (Chr(60) + "div id=\"player\">"+Chr(60)+"/div>")
jstring = YouTubeAPI.description
jstring = Replace(jstring,Chr(60)+"br/"+Chr(62),"\n")
jstring = Replace(jstring,"«",Chr(60))
jstring = Replace(jstring,"»",Chr(62))
JS.eval (jstring)

That should look like this with Code View still open after pasting:

image


After you click "OK", it will look like this:

image


STEP 4

This is the part where we call that function to open a video.

I'm not sure how your stuff is set up, so I'm just going to create a TEST command which will call that function.

image


image


NOW FOR A TEST DRIVE

Start the game and enter the command TEST once play begins.

https://play2.textadventures.co.uk/Play.aspx?id=editor/f3dba380-af21-429e-a87f-36ab5a8a8154%2fYouTube+API+Example+Game.aslx


ALSO NOTE:

I did not use mrangel's method (calling the JS from the UI Init script) because I don't know if you want the video to load when play begins.


Thank you K.V. it is something I can work with.

I will play with it some more today.

Tomorrow morning I am being dropped off in Terlingua, a small ghost town that has grown into a small community in recent decades. Great place to be homeless it seems, I will figure it out. https://www.google.com/maps/place/Terlingua,+TX+79852/@29.3215855,-103.6160191,15z/data=!3m1!4b1!4m5!3m4!1s0x86edf9745881eb03:0x134b70c0b975c160!8m2!3d29.321586!4d-103.6160191?hl=en

So, I may not be playing with this as much until I get myself settled in my new place.

The life of an itinerate philosopher. (let's be honest, vagabond)

So, hopefully my next post here is a success story and the game can begin growing.

One Question

As this develops, I am going to want to be able to make function calls from quest, or pass StringLists of video ids, while inside the JS.eval() script.  This will allow for changes in video playlists, and other features.  Is that all going to be through the ASLEvent code?

K.V.

As this develops, I am going to want to be able to make function calls from quest, or pass StringLists of video ids, while inside the JS.eval() script. This will allow for changes in video playlists, and other features. Is that all going to be through the ASLEvent code?

Mmm... Theoretically, but, honestly, that is some of the most complicated stuff one could attempt in the online editor.

If I were starting with, let's say, video 1, and the player had to watch that before getting access to video 2, I would handle all of this completely differently.

I would probably make two out-of-play objects (out-of-play: meaning not found in any location during play), and I would put video objects inside of those.

I would have playlist_object and video_archive. (The names don't really matter. Name them whatever.) These are the two out-of-play objects which hold the videos.

For the first video, I would create an object named video1. For its description, I would use this for the script (substituting the proper video ID):

ShowYouTube ("M7lc1UVf-VE")

Whoops. Sorry about that. I work in Code View...

That looks like this in the GUI:

image


Now, put video1 inside of playlist_object.

Basically, every video inside of playlist_object can be watched during play. The video objects inside of video_archive cannot be accessed by the player. Once the player reaches a point where video2 is unlocked, we can just move video2 from video_archive to playlist_object, like so:

MoveObject (video2, playlist_object)

From there, it depends upon how you have things set up.

If the player is using an in-game phone object to access these videos, you could just rename playlist_object to Video Directory or something like that. Make the phone a container. Make Video Directory a container which is inside of the phone object, and when the player enters X VIDEO DIRECTORY, it would show a list of all the videos inside of it. Then the player could just enter X VIDEO1 or X VIDEO2.


There are probably more than 100 ways to do it, but, if you want to keep up with which videos get watched to add videos to the game, I'd suggest something more like what I just described. All that Javascript (with YouTubeAPI) will probably end up driving you completely insane, even if you are already fluent in JS and Quest.


don't sweat the screen shots, I only work in code view, the GUI is a lost slower.

Cooking a last dinner here at this old fort/WWII POW camp officers headquarters in Marfa texas.

The API is so that I can trigger events based on video viewing. Because there are going to be entire playlists, and perhaps I can get a picture to show up at a certain time when the video is playing, slow down playback to highlight something, there are a lot of benefits to having the API.

Call it code camp.

Thanks again!


I think I have the gist of ASLEvents figured out, and now that the YouTube player is working, I can mess around inside this framework to make something that sparkles, hopefully. And now that I have an understanding of how to leverage Quest objects to run Javascript API, it might get interesting.

I created a function testASLEvent(test_) that takes on parameter

msg (test_)
  /* 4. The API will call this function when the video player is ready. */
      function onPlayerReady(event) {
        event.target.playVideo();
      var test = "bing bong";  <- something to display
      ASLEvent("testASLEvent", test); <- call ASLEvent
      }

Ran the test and got the video and a bing bong.

Cheers. Something to think about now. I will probably use an extended version of your idea of having a video repository object that holds lists of videos, and another object that deals with currently cued videos.

A quest game can only be 20MB? I don't think this little bit of code is going to become a problem, but that is good to know.

Thanks again K.V.


Running more tests,

I added this a function called EditYouTubeAPI

ClearScreen
jstring = YouTubeAPI.description
jstring = Replace(jstring, "test = \"bing bong\"", "test = \"bong bing\"")
YouTubeAPI.description = jstring

and it replaces the variable assignment test here

 /* 4. The API will call this function when the video player is ready. */
      function onPlayerReady(event) {
        event.target.playVideo();
      var test = "bing bong"; 
      ASLEvent("testASLEvent", test);
      }

This is the easiest way I know of to change a playlist. I just need to convert a stringlist into the form needed for a playlist in the API. I can work on that later.

The issue I am having, I can't get another iframe to start when I run test again.

I can run it once, I get a video and the ASLEvent output, but I cannot use the test command again to load another video.

I am going to have to think about this. because I am going to want to be able to call up another video at some point.

But, when I type
test
a second time, the output in quest is blank.

Any ideas? Is the current iframe code running and preventing a new iframe from being created?


I looked up remove() for iframe
found this
https://stackoverflow.com/questions/31808185/how-to-delete-html-elements-in-an-iframe-using-javascript

You can use

$("#iFrameId").contents().find("#yourDiv").empty();
It is better to use remove()

example: $("#iFrameId").contents().find("#yourDiv").remove();

Explaination

empty() will remove all the contents of the selection.

remove() will remove the selection and its contents and all the event handlers associated with it.

For reference: 1) http://api.jquery.com/remove/ 2) http://api.jquery.com/empty/

I also found this, so I can set the iframe id, but I would still need to set a div in my code to remove it it seems
https://www.dyn-web.com/tutorials/iframes/dyn-gen/

var ifrm = document.createElement('iframe');
ifrm.setAttribute('id', 'ifrm'); // assign an id

//document.body.appendChild(ifrm); // to place at end of document

// to place before another page element
var el = document.getElementById('marker');
el.parentNode.insertBefore(ifrm, el);

// assign url
ifrm.setAttribute('src', 'demo.html');

and this has some info that may lead to answer for finding the iframe id
https://www.codeproject.com/Questions/597144/howplustoplusgetplustheplusiframeplusid


The onYouTubeIframeAPIReady() is called when the API is succesfully loaded for the first time. The script to create a player should not be in that function unless you only want one player.

You should have the code to start the API in your UI initialisation script so that the API is ready to be used. Then, when you want to actually display a video, you do the new YT.Player bit.


And Quest can only take long string as function commands?
So I have to setup the onYouTubeIframeAPIReady() in a variable, jstring, for instance, along with the API asynchronous download bit, and then run
JS.eval(jstring)

I tried moving the code in question to the UI Init script, and it throws an error. Putting the raw code in the JS.eval() throws an error.

Moved the code into a room description and put a function call in the UI init that has the jstring = room.description
jstring = replace()
JS.eval(jstring)
bit,
and it works, but still only one player and it won't load new videos.

Do, I need to give the iframe and div id tags and setup a new object with code to invoke a new video inside the same player?
because even moving the code to the UI Init, at least the way I have gotten it to work, is causing the same problem, and now it won't stop playing the video at the specified time.


K.V.

And Quest can only take long string as function commands?

Negative.

The JS.eval() function takes a single string as a parameter.

Quest reads each line of code like it's Quest code.

So, this would not work in a Quest script:

JS.eval("function testMe(txt){
  alert (txt);
  };
  testMe('hello, world!');
")

...but this would:

JS.eval("function testMe(txt){  alert (txt);  };  testMe('hello, world!');")

...and so would this:

s = "function testMe(txt){"
s = s + "  alert (txt);"
s = s + "  };"
s = s + "  testMe('hello, world!');"
JS.eval(s)

...and the room.description attribute is a single string. So we can feed that to JS.eval() like this:

JS.eval(room.description)

EDIT

BUT, if your room description has line breaks, that will show up as <br/> within the JS code, and that would cause problems. Which is why mrangel does this:

jstring = room.description
jstring = Replace(jstring,Chr(60)+"br/"+Chr(62),"\n")
jstring = Replace(jstring,"«",Chr(60))
jstring = Replace(jstring,"»",Chr(62))
JS.eval (jstring)

I recommend completing the tutorial before proceeding any further. It's quite short and teaches almost everything you'll need to know about Quest.

http://docs.textadventures.co.uk/quest/tutorial/creating_a_simple_game.html


Also, I know I said this before, but you are messing with some of the most complicated stuff you could mess with in a Quest game.

...and that is the truth.

Passing things to and from JS can be very frustrating, even for experienced users.

The screen will scroll the video up with every command, too. So you'll need to learn how to play with the CSS settings and how Quest will drive you insane while trying to do so.

Plus, I don't think anyone here (including myself) is familiar with YouTube API.

I'm not saying you won't succeed. I'm just saying doing the tutorial game first will help you avoid many, many frustrating obstacles. From there, you'll probably be unstoppable.


Do, I need to give the iframe and div id tags and setup a new object with code to invoke a new video inside the same player?

You can create a new player whenever you want one.
Or you can cause an existing player to load a new video.

You can do whichever works best for the game you want to create. I'd probably go with the latter.

Here's how I'd arrange the code off the top of my head:

A ton of untested code
$(function (){
  // some frame-global variables to keep track of state
  var pendingVideos = [];
  var lastPlayer;
  var ytApiLoaded = 0;
  var playerid = 1;
  var pendingPlayer;

  // Called on startup
  // this doesn't create a single player like the example code
  // instead it just starts playing any videos that Quest added to the queue before our connection to youtube was established
  onYouTubeIframeAPIReady = function () {
    ytApiLoaded = 1;
    if (pendingVideos.length) {
      CreateYoutubePlayer();
    }
  };

  QueueYoutubeVideo = function (id) {
    if (lastPlayer) {
      if (lastPlayer.getPlayerState() == 1) {
        lastPlayer.cueVideoById(id);
      } else {
        lastPlayer.loadVideoById(id);
      }
    } else {
      pendingVideos.push(id);
      CreateYoutubePlayer();
    }
  };

  onPlayerReady = function (event) {
    var player = event.target;
    player.playVideo();
    if (pendingVideos) {
      $.each(pendingVideos, function (i, id) { player.cueVideoById(id); });
      pendingVideos = [];
    }
  };

  var StateChanged = function(event) {
    var player = event.target;
    switch (event.data) {
      case 0:
        ASLEvent ('YoutubeVideoFinished', player.getVideoUrl());
        break;
      case 2:
        ASLEvent ('YoutubeVideoPaused', player.getVideoUrl());
        break;
      case 3:
        ASLEvent ('YoutubeVideoBuffering', player.getVideoUrl());
        break;
    }
  };

  CreateYoutubePlayer = function(settings) {
    if (lastPlayer) {
      if (lastPlayer.getPlayerState()) {
        lastPlayer.pauseVideo();
      }
    }
    if (!settings) {
      settings = pendingVideos.shift();
    }
    if (typeof settings === 'string') {
      settings = {videoId: settings};
    }
    settings = $.extend({width: 640, height: 480, events: {onReady: onPlayerReady, onStateChange: StateChanged}}, settings);
    if (!pendingPlayer) {
      while ($('#playerElement'+playerid).length) { playerid++; }
      settings['id'] = 'playerElement'+playerid;
      AddText($('«div»', settings));
      pendingPlayer = 'playerElement' + playerId;
    }
    if (settings['videoId']) {
      lastPlayer = new YT.Player (pendingPlayer, settings);
      pendingPlayer = null;
    }
  };

  $('«script»', {src:'https://www.youtube.com/iframe_api'}).prependTo('head');
});

...

OK, I started typing that and didn't realise how long it would take. Not tested.

Putting that in your room description (with the code in the UI initialisation script to call it) should mean that unless I made any mistakes, you can then do (from Quest):

  • JS.CreateYoutubePlayer("some-id") - creates a new player and starts a given video (pausing the previous player if it's still playing, so you don't have 2 players running at once)
  • JS.QueueYoutubeVideo("some-id") - adds a video to the end of the current playlist for the last player displayed; or creates a new player if there isn't one open.

The ID for those can either be a string containing the ID; or a stringdictionary with the keys "videoId", "startSeconds", and "endSeconds"

You could also create Quest functions YoutubeVideoPaused, YoutubeVideoBuffering and YoutubeVideoFinished which take a single parameter (a video URL - the API doesn't seem to give out the video ID easily) if you want Quest to respond to those events.


A YouTube id I would supply.
An example: https://youtu.be/iyhMKRpw6EM

https://youtu.be/---->iyhMKRpw6EM
Extra parameters follow the ?
That's what I know.

I would like to either, pass these ids into variables and string lists, and then  use those variables and string lists to format ids properly for JS.eval() and quest.

Or, write those variables and strings into different room descriptions, 
Example 

currentVidoesObj
queuedVideosObj
watchedVideosObj
playerPuzzleBoxVideosObj
pictureGalleryObj
soundStudioObj

storing variables and lists for each video in the object, loading the information to either
YouTubeIframAPI 
or
*.?Obj.description 

I don't know what's happening to the code at the web GUI level, so all results are unexpected. It's an interesting adventure.
The Gui is nice for targeted edits, but I prefer code view.

Thanks mrangel for the function descriptions.

And Thank You KV for the function code.

This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums