Warm tip: This article is reproduced from stackoverflow.com, please click
javascript shiny youtube-api

Get Current Time of Youtube Video Embedded in a Shiny app

发布于 2020-03-27 10:20:19

I'm trying to store the current time of an embedded YouTube video in a shiny app upon a button click. Eventually I'd like to be able to bring that time data back into my R environment, but for now I'm just struggling with how to use the YouTube API within shiny.

I know from looking at the YouTube API and from this question that you can get the current time of an embedded YouTube video. But when I wrap that video in the Shiny interface, the video has class shiny-html-output shiny-bound-output and I can't seem to find the underyling element that corresponds to the YouTube video that's controllable by the api.

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs()

  ,titlePanel("Hello Shiny!")

  ,sidebarLayout(

    sidebarPanel(
      actionButton("button", "Capture Video Time")
    ),

    mainPanel(
      uiOutput("video")
    )
  )
)

server <- function(input, output) {

  output$video <- renderUI({
    HTML('<iframe width="560" height="315" src="https://www.youtube.com/embed/FR4QIeZaPeM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>')
  })

  observeEvent(input$button, {
    runjs(
      "ytplayer = document.getElementById('video');
      var time = ytplayer.getCurrentTime();
      alert(time);"
    )
  })
}

shinyApp(ui = ui, server = server)

What I expect is that when I click the button in the Shiny app, a window should pop up with the current time of the YouTube video, but instead nothing happens. So what my biggest need is to get that javascript code right inside the runjs() function (or something else if runjs() isn't appropriate here) so that I can actually find the video player's time. If you have any insight on how to bring that current time value back into my R environment it would be appreciated as well, but I can probably figure that part out once I get on the right track with this. Thanks so much in advance!

Questioner
Pete M
Viewed
34
Pete M 2019-07-09 01:26

I was able to figure it out myself. The issue was that I didn't set up the YouTube iframe API properly. Once I got that in place the rest of the code worked as I expected it to. For reference here is the documentation on the YouTube iframe API.

My working code is as follows:

library(shiny)
library(shinyjs)

ui = shinyUI(fluidPage(
  useShinyjs(),
  headerPanel("New Application"),
  sidebarPanel(
    actionButton("getTime","Get Video Time")
  )
  ,mainPanel(
    uiOutput("video")
  )
))

server = function(input, output) {

  output$video <- renderUI({
    HTML(
      '<html>
        <body>
          <iframe id="existing-iframe"
              width="640" height="360"
              src="https://www.youtube.com/embed/fmuUQCB3pAE?enablejsapi=1"
              frameborder="0"
          ></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(\'existing-iframe\');
            }
          </script>
        </body>
      </html>'
    )
  })

  observeEvent(input$getTime,{
    runjs("alert(player.getCurrentTime())")
  })
}

shinyApp(ui = ui, server = server)

Note the major difference is inside the HTML() wrapper beginning on line 18. I set up the iframe the same way but with id="existing-iframe" along with enablejsapi=1 inside the URL. Then I needed the <script> tag below which had all of the JavaScript code I needed to get the app to recognize that I was using the YouTube iframe api. I also cleaned up the code inside the runjs() wrapper on line 44.