问题
I am creating a shiny app that will do the following using Spotify's API:
1) You manually type in the Artist Name
2) In the selectInput, the albums need to automatically populate for your selection.
3) After selecting the album, a table in the main panel will show the songs, artist, and album.
I have this working so far but I cannot figure out how to do the 2nd part which is to automatically populate the albums once the artist is selected. I asked a previous question here: Shiny: Automatic SelectInput Value Update Based on Previous Filter but I realized that after asking the question, the dataset will not be known in the beginning for you to reference in the ui.
So instead of doing this:
selectInput("selectinputid", "Album #1 to Select:", choices = c("Yeezus" = "Yeezus", "Graduation" = "Graduation", "Gears" = "gear"))
I want to do this:
selectInput("selectinputid", "Album #1 to Select:", choices = unique(with_album_name$`Album Name`)
Here is the code:
# ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("Spotify: Interactive Song Selection"),
sidebarLayout(
sidebarPanel(
helpText("The goal from this is for you to compare two artists' albums and see how similar the songs are based on the audio features."),
helpText("Select your first artist you want to compare. For example: ",
tags$b("Kanye West")),
textInput("albumId", "Artist Name #1", value = "", width = NULL,
placeholder = NULL),
actionButton("goButton", "Submit Both Artists"),
helpText("Based on the artist you selected, now select the albums that you want to compare songs for."),
selectInput("selectinputid", "Album #1 to Select:", choices = c("Yeezus" = "Yeezus", "Graduation" = "Graduation", "Gears" = "gear")),
actionButton("goButton1", "Submit Both Albums")),
mainPanel(
tableOutput("result")
)
)
))
Server Section:
# server.R
spotifyKey <- "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
spotifySecret <- "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
library("httr")
library("jsonlite")
library(ggplot2)
library(scales)
library(dplyr)
response = POST(
'https://accounts.spotify.com/api/token',
accept_json(),
authenticate(spotifyKey, spotifySecret),
body = list(grant_type = 'client_credentials'),
encode = 'form',
verbose()
)
token = content(response)$access_token
HeaderValue = paste0('Bearer ', token)
library(shiny)
shinyServer(function(input, output) {
output$result <- renderTable({
randomVals <- eventReactive(input$goButton, input$albumId)
spotify <- c(randomVals())
##Retrieve Artist ID
get.artist <- function(spotify){
artistnameURL <- paste("https://api.spotify.com/v1/search?q=", spotify, "&type=artist", sep="")
getArtist <- GET(artistnameURL, add_headers(Authorization = HeaderValue))
artistname <- jsonlite::fromJSON(toJSON(content(getArtist)))
ids <- data.frame(matrix(unlist(artistname$artists$items$id),
nrow=artistname$artists$total,
byrow=T),stringsAsFactors=FALSE)
names <- data.frame(matrix(unlist(artistname$artists$items$name),
nrow=artistname$artists$total,
byrow=T),stringsAsFactors=FALSE)
colnames(ids)[1]<-"Artist ID"
colnames(names)[1]<-"Artist Name"
artist_search <- cbind(names, ids)
artist_search <- artist_search[1,]
return(artist_search)
}
df1 <- lapply(spotify, get.artist)
result2 <- do.call(rbind, df1)
result2_final<-result2
ids<-result2_final$`Artist ID`
##Retrieve Artist Albums
get.albums <- function(ids){
artists_albumsURL <- paste("https://api.spotify.com/v1/artists/", ids, "/albums", sep="")
getArtistAlbum <- GET(artists_albumsURL, add_headers(Authorization = HeaderValue))
artistalbumname <- jsonlite::fromJSON(toJSON(content(getArtistAlbum)))
albumids <- data.frame(matrix(unlist(artistalbumname$items$id),
nrow=artistalbumname$total,
byrow=T),stringsAsFactors=FALSE)
albumnames <- data.frame(matrix(unlist(artistalbumname$items$name),
nrow=artistalbumname$total,
byrow=T),stringsAsFactors=FALSE)
artistid2 <- data.frame(matrix(unlist(artistalbumname$items$artists[[1]]$id),
nrow=artistalbumname$total,
byrow=T),stringsAsFactors=FALSE)
artistname2 <-
data.frame(matrix(unlist(artistalbumname$items$artists[[1]]$name),
nrow=artistalbumname$total, byrow=T),stringsAsFactors=FALSE)
colnames(albumids)[1]<-"Album IDs"
colnames(albumnames)[1]<-"Album Name"
colnames(artistid2)[1]<-"Artist ID"
colnames(artistname2[1])<-"Artist Name"
album_search <- cbind(artistid2, artistname2, albumnames, albumids)
album_search <- unique(album_search)
return(album_search)
}
df <- lapply(ids, get.albums)
result <- do.call(rbind, df)
result_final<-result
colnames(result_final)[2]<-"Artist Name"
spotify<-result_final$`Album IDs`
get.tracks <- function(spotify){
albumTracksURL <- paste("https://api.spotify.com/v1/albums/", spotify, "/tracks?limit=50", sep="")
getTracks <- GET(albumTracksURL, add_headers(Authorization = HeaderValue))
albumTracks <- jsonlite::fromJSON(toJSON(content(getTracks)))
ids <- data.frame(matrix(unlist(albumTracks$items$id),
nrow=albumTracks$total, byrow=T),stringsAsFactors=FALSE)
names <- data.frame(matrix(unlist(albumTracks$items$name),
nrow=albumTracks$total, byrow=T),stringsAsFactors=FALSE)
artists<-albumTracks$items$artists
artists1<-do.call(rbind, lapply(artists, function(x) do.call(cbind, lapply(x[c('id', 'name')], toString))))
result <- cbind(ids, names, artists1)
colnames(result) <- c("ID", "NAME", "ARTIST ID", "ARTIST NAME")
result$AlbumID <- spotify
return(result)
}
df <- lapply(spotify, get.tracks)
result <- do.call(rbind, df)
result_final2<-result
names(result_final2) <- c("ID", "NAME", "ARTIST ID", "ARTIST NAME")
final<-result_final2
final1<-final[!duplicated(final), ]
final2 <- final1[!duplicated(final1[2:5]),]
colnames(final2)[5]<-"Album ID"
with_album_name<-left_join(final2,result_final, by=c("Album ID" = "Album IDs"))
with_album_name <- with_album_name[,-c(6:7)]
##target <- c(input$selectinputid)
randomVals2 <- eventReactive(input$goButton1, input$selectinputid)
target <- c(randomVals2())
result_final<-filter(with_album_name, `Album Name` %in% target)
final2<-result_final
final2
})})
Output:
In the ui part, I need to find a way to reference the with_album_name
table, but that table is created in the server section. Not sure how this can be done because right now all I have is to manually put in the album names for selection and that will not work when I want to reference a different artist in the spotify database.
回答1:
I overwrote your server part:
moved all functions out of it (I prefer to keep my server function in a separate file);
Restructured server code: all code was returning single table. I now added
observeEvent
to update list
Minor changes in selectInput
part:
selectInput("selectinputid", "Album #1 to Select:", "")
Server part:
server <- function(input, output, session) {
ids <- reactive({
randomVals <- eventReactive(input$goButton, input$albumId)
spotify <- c(randomVals())
df1 <- lapply(spotify, get.artist)
result2 <- do.call(rbind, df1)
result2_final<-result2
result2_final$`Artist ID`
})
result_final <- reactive({
df <- lapply(ids(), get.albums)
result <- do.call(rbind, df)
colnames(result)[2]<-"Artist Name"
result
})
# Observes and updates album selection part
observe({
albums <- unique(result_final()$`Album Name`)
updateSelectInput(session, "selectinputid",
label = "selectinputid",
choices = albums,
selected = albums[1])
})
output$result <- renderTable({
spotify<-result_final()$`Album IDs`
df <- lapply(spotify, get.tracks)
result <- do.call(rbind, df)
result_final2<-result
names(result_final2) <- c("ID", "NAME", "ARTIST ID", "ARTIST NAME")
final<-result_final2
final1<-final[!duplicated(final), ]
final2 <- final1[!duplicated(final1[2:5]),]
colnames(final2)[5]<-"Album ID"
with_album_name<-left_join(final2,result_final(), by=c("Album ID" = "Album IDs"))
with_album_name <- with_album_name[,-c(6:7)]
randomVals2 <- eventReactive(input$goButton1, input$selectinputid)
target <- c(randomVals2())
result_final<-filter(with_album_name, `Album Name` %in% target)
final2<-result_final
final2
})
}
All code (messy):
library(shiny)
ui <- fluidPage(
titlePanel("Spotify: Interactive Song Selection"),
sidebarLayout(
sidebarPanel(
helpText("The goal from this is for you to compare two artists' albums and see how similar the songs are based on the audio features."),
helpText("Select your first artist you want to compare. For example: ",
tags$b("Kanye West")),
textInput("albumId", "Artist Name #1", value = "", width = NULL,
placeholder = NULL),
actionButton("goButton", "Submit Both Artists"),
helpText("Based on the artist you selected, now select the albums that you want to compare songs for."),
selectInput("selectinputid", "Album #1 to Select:", ""),
actionButton("goButton1", "Submit Both Albums")),
mainPanel(
tableOutput("result")
)
)
)
spotifyKey <- "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
spotifySecret <- "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
library("httr")
library("jsonlite")
library(ggplot2)
library(scales)
library(dplyr)
response = POST(
'https://accounts.spotify.com/api/token',
accept_json(),
authenticate(spotifyKey, spotifySecret),
body = list(grant_type = 'client_credentials'),
encode = 'form',
verbose()
)
token = content(response)$access_token
HeaderValue = paste0('Bearer ', token)
get.artist <- function(spotify){
artistnameURL <- paste("https://api.spotify.com/v1/search?q=", spotify, "&type=artist", sep="")
getArtist <- GET(artistnameURL, add_headers(Authorization = HeaderValue))
artistname <- jsonlite::fromJSON(toJSON(content(getArtist)))
ids <- data.frame(matrix(unlist(artistname$artists$items$id),
nrow=artistname$artists$total,
byrow=T),stringsAsFactors=FALSE)
names <- data.frame(matrix(unlist(artistname$artists$items$name),
nrow=artistname$artists$total,
byrow=T),stringsAsFactors=FALSE)
colnames(ids)[1]<-"Artist ID"
colnames(names)[1]<-"Artist Name"
artist_search <- cbind(names, ids)
artist_search <- artist_search[1,]
return(artist_search)
}
get.albums <- function(ids){
artists_albumsURL <- paste("https://api.spotify.com/v1/artists/", ids, "/albums", sep="")
getArtistAlbum <- GET(artists_albumsURL, add_headers(Authorization = HeaderValue))
artistalbumname <- jsonlite::fromJSON(toJSON(content(getArtistAlbum)))
albumids <- data.frame(matrix(unlist(artistalbumname$items$id),
nrow=artistalbumname$total,
byrow=T),stringsAsFactors=FALSE)
albumnames <- data.frame(matrix(unlist(artistalbumname$items$name),
nrow=artistalbumname$total,
byrow=T),stringsAsFactors=FALSE)
artistid2 <- data.frame(matrix(unlist(artistalbumname$items$artists[[1]]$id),
nrow=artistalbumname$total,
byrow=T),stringsAsFactors=FALSE)
artistname2 <-
data.frame(matrix(unlist(artistalbumname$items$artists[[1]]$name),
nrow=artistalbumname$total, byrow=T),stringsAsFactors=FALSE)
colnames(albumids)[1]<-"Album IDs"
colnames(albumnames)[1]<-"Album Name"
colnames(artistid2)[1]<-"Artist ID"
colnames(artistname2[1])<-"Artist Name"
album_search <- cbind(artistid2, artistname2, albumnames, albumids)
album_search <- unique(album_search)
return(album_search)
}
get.tracks <- function(spotify){
albumTracksURL <- paste("https://api.spotify.com/v1/albums/", spotify, "/tracks?limit=50", sep="")
getTracks <- GET(albumTracksURL, add_headers(Authorization = HeaderValue))
albumTracks <- jsonlite::fromJSON(toJSON(content(getTracks)))
ids <- data.frame(matrix(unlist(albumTracks$items$id),
nrow=albumTracks$total, byrow=T),stringsAsFactors=FALSE)
names <- data.frame(matrix(unlist(albumTracks$items$name),
nrow=albumTracks$total, byrow=T),stringsAsFactors=FALSE)
artists<-albumTracks$items$artists
artists1<-do.call(rbind, lapply(artists, function(x) do.call(cbind, lapply(x[c('id', 'name')], toString))))
result <- cbind(ids, names, artists1)
colnames(result) <- c("ID", "NAME", "ARTIST ID", "ARTIST NAME")
result$AlbumID <- spotify
return(result)
}
server <- function(input, output, session) {
ids <- reactive({
randomVals <- eventReactive(input$goButton, input$albumId)
spotify <- c(randomVals())
df1 <- lapply(spotify, get.artist)
result2 <- do.call(rbind, df1)
result2_final<-result2
result2_final$`Artist ID`
})
result_final <- reactive({
df <- lapply(ids(), get.albums)
result <- do.call(rbind, df)
colnames(result)[2]<-"Artist Name"
result
})
# Observes and updates album selection part
observe({
albums <- unique(result_final()$`Album Name`)
updateSelectInput(session, "selectinputid",
label = "selectinputid",
choices = albums,
selected = albums[1])
})
output$result <- renderTable({
spotify<-result_final()$`Album IDs`
df <- lapply(spotify, get.tracks)
result <- do.call(rbind, df)
result_final2<-result
names(result_final2) <- c("ID", "NAME", "ARTIST ID", "ARTIST NAME")
final<-result_final2
final1<-final[!duplicated(final), ]
final2 <- final1[!duplicated(final1[2:5]),]
colnames(final2)[5]<-"Album ID"
with_album_name<-left_join(final2,result_final(), by=c("Album ID" = "Album IDs"))
with_album_name <- with_album_name[,-c(6:7)]
randomVals2 <- eventReactive(input$goButton1, input$selectinputid)
target <- c(randomVals2())
result_final<-filter(with_album_name, `Album Name` %in% target)
final2<-result_final
final2
})
}
shinyApp(ui, server)
来源:https://stackoverflow.com/questions/46352515/reactive-selectinput-dataset-unknown-until-api-call-from-previous-filter