0
votes

I'm developing a Shiny application that will allow users to explore a music database and then view the results of their selection across a series of tabs, one of which is a data table. Within that data table is a variable containing a YouTube link.

When users click on a particular YT link I would like that video to play within the Shiny interface. The user could then continue to explore the Shiny app as the video plays in the background, but if they then click another YT link a new video would play. Essentially I'm trying to create a responsive YouTube player within a Shiny app.

I've created a basic version of the app here with some dummy data. I've been able to make the URL info in the database an actionable link that opens in a new window, but I'd like it to open/play within the YouTube Render tab instead. I've created that tab with a YouTube video using a link to demonstrate what I would like - the video plays in the background if you switch back to the datatable tab.

What I'm unable to do is link user actions (clicks on the links in the data table) with what happens on the YouTube Render tab.

EDIT: I'm grateful for the responses pointing me in the direction of what appear to be two similar questions/responses. I'd previously looked at both questions and neither appear to answer my question. My apologies if I wasn't clear in my original description, or have misunderstood the replies to the related questions, but this case is slightly different.

In Embed instagram/youtube into Shiny R app the solution refers to one video that plays if a particular data point within a visualisation is clicked. Clicking the other data point in that example does not trigger an alternate video (I amended the code from www.anotherlink.com to a YouTube link, and nothing changed). In the other question/solution embed iframe inside shiny app the user makes the choice in the sidebar and a url is completed by the content of that choice. In my instance the link will appear in an output in the main panel (as in the first example), but the user will have multiple options since the output is a data frame generated by their selections in the side panel. In my example code the user can select links of Type A or B, which alters the content of the data frame returned, but whether choosing A or B they still have two links to choose from. My question is this:

if the user clicks on one of those two links (or four, if they have selected both link types in the sidebar), how do I code the YouTube render output tab in such a way that the video link click is selected as the content for the video tab, and further, how does that URL selection change reactively if the user then clicks another link.

It seems that something along the lines of click = "plot_click" would be needed, but I what suppose I'm asking is how, on the server side, the input generated by that click (i.e. a URL) is then selected from one of n links available from the reactive data frame created by the user's selection in the side panel. For context, my data frame has c 20k rows, and thus 20k You Tube links, that the user can choose from based on a number of parameters in the sidebar.

Hopefully this is now more clearly explained and someone has a solution. Thanks.

library(shiny)
library(DT)
library(shinyWidgets)

#Create Data Frame
type <- c("A", "A", "B", "B")
link <- c("Link 1", "Link 2", "Link 3", "Link 4")
url <- c("https://www.youtube.com/watch_popup?v=-CPXRfwaHaM",
         "https://www.youtube.com/watch?v=YMpQFbe9Tho",
         "https://www.youtube.com/watch?v=N0mh-JlGxzY",
         "https://www.youtube.com/watch?v=osjA0b5ktKU")

data <- data.frame(type, link, url)

#Create Choices for Sidebar
types <- unique(data$type)

#Create URL links for Datatable
target_blank <- ' target="_blank"'
data$url <- paste0("<a ", target_blank, " href='",data$url,"'>", data$url,"</a>")

ui <- fluidPage(

    titlePanel("Data Table to External URL - Example"),

    sidebarLayout(
        sidebarPanel(
            pickerInput("type","Select Type", 
                        choices= types, 
                        options = list(`actions-box` = TRUE),
                        selected = type,
                        multiple = T)),

        # Main Panel
        mainPanel(
            tabsetPanel(type = "tabs", 
                        tabPanel("Table", DT::dataTableOutput(outputId = "table")),
                        tabPanel("You Tube Render", uiOutput("video"))
            ))
    )
)

server <- function(input, output) {

 data_sample <- reactive({
        req(input$type)
        data_sample <- data %>%
            filter(type %in% input$type)

    })

 output$table <- DT::renderDataTable({
     data_sample <- data_sample()
     DT::datatable(data = data_sample, 
                   options = list(pageLength = nrow(data_sample)),
                   colnames=c("Type", "Link Name", "URL"),
                   rownames = FALSE, escape = FALSE)
 })

 output$video <- renderUI({
     HTML(
         '<html>
        <body>
          <iframe id="existing-iframe"
              width="100%" height="360"
              src="https://www.youtube.com/watch_popup?v=-CPXRfwaHaM" ###This URL needs to change dynamically based on which link the user clicks in output$table
              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>'
     )
 })
}

shinyApp(ui = ui, server = server)
1
Thanks @maydin. I've edited my original question as I'm not sure either of those previous questions precisely answer my question. Thanks.Craig Hamilton

1 Answers

0
votes

Looking at the documentation here you can get the row which is selected by using the input binding input$id_rows_selected. We can then filter the dataframe and get the link which was selected.

library(shiny)
library(DT)
library(shinyWidgets)

#Create Data Frame
type <- c("A", "A", "B", "B")
link <- c("Link 1", "Link 2", "Link 3", "Link 4")
url <- c("https://www.youtube.com/watch_popup?v=-CPXRfwaHaM",
         "https://www.youtube.com/watch_popup?v=YMpQFbe9Tho",
         "https://www.youtube.com/watch_popup?v=N0mh-JlGxzY",
         "https://www.youtube.com/watch_popup?v=osjA0b5ktKU")

data <- data.frame(type, link, url)

#Create Choices for Sidebar
types <- unique(data$type)

#Create URL links for Datatable
target_blank <- ' target="_blank"'
data$url <- paste0("<a ", target_blank, " href='",data$url,"'>", data$url,"</a>")

ui <- fluidPage(
  titlePanel("Data Table to External URL - Example"),

  sidebarLayout(
    sidebarPanel(
      pickerInput("type","Select Type", 
                  choices= types, 
                  options = list(`actions-box` = TRUE),
                  selected = type,
                  multiple = T)),

    # Main Panel
    mainPanel(
      tabsetPanel(type = "tabs", 
                  tabPanel("Table", DT::dataTableOutput("table")),
                  tabPanel("You Tube Render", uiOutput("video"))
      ))
  )
)

server <- function(input, output) {

  data_sample <- reactive({
    req(input$type)
    data_sample <- data %>%
      filter(type %in% input$type)

  })

  output$table <- DT::renderDataTable({
    data_sample <- data_sample()
    DT::datatable(data = data_sample, 
                  options = list(pageLength = nrow(data_sample)),
                  colnames=c("Type", "Link Name", "URL"),
                  rownames = FALSE, escape = FALSE)
  })

  output$video <- renderUI({
    req(data_sample())

    selection = input$table_rows_selected
    urls = data_sample()$url
    url = if(length(selection) != 0) urls[selection[length(selection)]] else urls[1]

    url = gsub(".*href='(.*)'>.*$","\\1",url)

    HTML(paste0(
      '<html>
        <body>
          <iframe id="existing-iframe"
              width="100%" height="360"
              src="',url,'" ###This URL needs to change dynamically based on which link the user clicks in output$table
              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>'
    ))
  })
}

shinyApp(ui = ui, server = server)