rshinyyoutube-apishinyjs

Why are shinyjs' functions not working when videos are randomly selected?


I want to make a Shiny app that randomly selects one of two videos and, only after the video has been played, produces a radio button asking if people have watched the video.

This code works fine with one video, shown here:

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  uiOutput("video"),
  hidden(
    radioButtons(
      "instructions",
      "Have you finished watching the instructions?",
      choices = list(
        "Yes" = 'finished_instructions'
      ),
      width = '100%',
      selected = character(0)
    )
  )
)
server <- function(input, output) {
  
  output$video <- renderUI({
    HTML(
      '<html>
        <body>
          <iframe id="my-iframe"
              width="560" height="315"
              src="https://www.youtube.com/embed/-CrOeCzMFSQ?si=3pKfFn_AnvVXKASX?&enablejsapi=1" 
              frameborder="0"
              allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
          ></iframe>
          
          <script type="text/javascript">
            var tag = document.createElement("script");
            tag.src = "https://www.youtube.com/iframe_api";
            var firstScriptTag = document.getElementsByTagName("script")[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            var player;
            function onYouTubeIframeAPIReady() {
              player = new YT.Player("my-iframe");
              
              player.addEventListener("onStateChange", function(state){
                if (state.data === 0) {
                  document.getElementById("instructions").classList.remove("shinyjs-hide");
                }
              });
            }  
          </script>
        </body>
      </html>'
    )
  })
}

shinyApp(ui, server)

However, no matter how I've coded it, when I attempt to implement this with more than one video, the radio buttons simply never appear. The broken code is shown below - the video(s) will populate, but unlike in the above example, the radio buttons never appear. Any thoughts on how to fix this?

library(shiny)
library(shinyjs)

ui <- fluidPage(
  
  
  useShinyjs(),
  uiOutput("video"),
  hidden(
    radioButtons(
      "instructions",
      "Have you finished watching the instructions?",
      choices = list(
        "Yes" = 'finished_instructions'
      ),
      width = '100%',
      selected = character(0)
    )
  )
)
server <- function(input, output) {

vec2 <- c(1,2)
arm_condition <- sample(vec2,1)

if (arm_condition == 1){
  output$video <- renderUI({
    HTML(
      '<html>
        <body>
          <iframe id="my-iframe"
              width="560" height="315"
              src="https://www.youtube.com/embed/-CrOeCzMFSQ?si=3pKfFn_AnvVXKASX?" 
              frameborder="0"
              allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
          ></iframe>
          
          <script type="text/javascript">
            var tag = document.createElement("script");
            tag.src = "https://www.youtube.com/iframe_api";
            var firstScriptTag = document.getElementsByTagName("script")[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            var player;
            function onYouTubeIframeAPIReady() {
              player = new YT.Player("my-iframe");
              
              player.addEventListener("onStateChange", function(state){
                if (state.data === 0) {
                  document.getElementById("instructions").classList.remove("shinyjs-hide");
                }
              });
            }  
          </script>
        </body>
      </html>'
    )
  })}
  
if (arm_condition == 2){
  output$video <- renderUI({
    HTML(
      '<html>
        <body>
          <iframe id="my-iframe"
              width="560" height="315"
              src="https://www.youtube.com/embed/HlYFfFUdPcw?si=v3V_FzhmndmYXJSS" 
              frameborder="0"
              allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
          ></iframe>
          
          <script type="text/javascript">
            var tag = document.createElement("script");
            tag.src = "https://www.youtube.com/iframe_api";
            var firstScriptTag = document.getElementsByTagName("script")[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            var player;
            function onYouTubeIframeAPIReady() {
              player = new YT.Player("my-iframe");
              
              player.addEventListener("onStateChange", function(state){
                if (state.data === 0) {
                  document.getElementById("instructions").classList.remove("shinyjs-hide");
                }
              });
            }  
          </script>
        </body>
      </html>'
    )
  })
}}

shinyApp(ui, server)

I've tried various versions of assigning the random operator, of randomly selecting one of two srcs, but nothing will get the radio buttons to appear when there are more than two videos. I've also tried specifically creating "video1" and "instructions1" and "video2" and "instructions2", but this did not work either.


Solution

  • You have to enable the JS api. This can be done by adding

    ?&enablejsapi=1
    

    at the end of the video links (see your first example). If you add this to your extended example on every link, it will work.