---
title: "Spotify - Track Analyses, Videos and Lyrics"
output: 
  flexdashboard::flex_dashboard:
    logo: "logo.png"
    orientation: columns
    vertical_layout: fill

    css: styles.css
    source_code: embed
runtime: shiny    
---

````{r, setup, warning= FALSE, echo=FALSE, error=FALSE, message= FALSE}
knitr::opts_chunk$set(eval=TRUE, echo=FALSE, error=FALSE, message= FALSE)

#Load required libraries

library(httr)
library(stringr)
library(tidyverse)
library(lubridate)
library(rvest)
library(tidytext)
library(scales)
library(RColorBrewer)
library(highcharter)
library(DT)
library(shiny)
library(vembedr)
library(plotly)
library(bsplus)

use_bs_tooltip()
use_bs_popover()

```

```{r sp_authorization,  echo=FALSE}
# Please obtain own authorization if using code
client_id <- "df4b4ced508a4ac39ea5357c3ed2d477"
  client_secret <- "48e05067ee3e47a4b9fcedea97ffa5ae"
  access_token <- POST('https://accounts.spotify.com/api/token',
                       accept_json(), authenticate(client_id, client_secret),
                       body = list(grant_type='client_credentials'),
                       encode = 'form', httr::config(http_version=2)) %>% content %>% .$access_token

  # removed as  error arising (probably not related thoughbut not sure why - vignette still works
 #  # this creates a url but when inputting into browser window
 #   enableBookmarking("url")
 # # Error in RestoreContext initialization: Failed to parse URL parameter "albumTable_search"
 #   
 #   #This gives copyable version which enters beatles and artist selection but not album selection
 #  enableBookmarking("server")
 #   setBookmarkExclude(c("bm1")) #Error in setBookmarkExclude(c("bm1")) : attempt to apply non-function 2001-08-21
 #   #setBookmarkExclude("bm1") didnt help
 #  
 #  # Trigger bookmarking 
 #  observeEvent(input$bm1, {
 #    session$doBookmark()
 #  })
 # 

```

Inputs {.sidebar data-width=250}
-------------------------------------



```{r artist, warning=FALSE}

# Introduction
#includeMarkdown("info.md")
hr()


# Select Artist

textInput("artist",
"Enter Artist",
value = "",
placeholder = "e.g. Laura Nyro") %>% 
  shinyInput_label_embed(
    shiny_iconlink() %>% 
    bs_embed_tooltip(title="Need First or Last name")) # may be a limit on word count


actionButton(inputId = "goArtist", label = "Get Artist Selection") 
  

hr()

artistData <- eventReactive(input$goArtist, {
req(input$artist)
req(input$artist)
# Search Spotify API for artist name
res <-
GET('https://api.spotify.com/v1/search',
query = list(q = input$artist, type = 'artist',access_token = access_token)) %>%
content %>% .$artists %>% .$items

# Clean response and combine all returned artists into a dataframe
artists <- map_df(seq_len(length(res)), function(x) {
list(
artist_name = res[[x]]$name,
artist_uri = str_replace(res[[x]]$uri, 'spotify:artist:', ''),
# remove meta info from the uri string
artist_img = ifelse(length(res[[x]]$images) > 0, res[[x]]$images[[1]]$url, NA)
)
})

return(artists)
})

output$artists <- DT::renderDataTable({
artistData() %>%
#select(artist=artist_name,artist_uri,artist_img) %>%
select(Choose_One_or_More = artist_name) %>%
DT::datatable(
class = 'compact stripe hover row-border order-column',


selection = "multiple",
rownames = FALSE,
options = list(
paging = TRUE,
searching = FALSE,
info = FALSE,
deferRender = TRUE,
scrollY = 200,
scroller = TRUE
)
)
})
br()
DT::dataTableOutput("artists")
br()
actionButton(inputId = "goArtists", label = "Get Albums")
# br()
# bookmarkButton(id = "bm1") # await other issue resolution - currently only returns first table output

```

```{r}
#Select Albums


allArtistsData <- eventReactive(input$goArtists, {
req(input$artists_rows_selected)
 print("enter")
s = as.integer(input$artists_rows_selected)
 print(s)

## should purrr this, presumably
for (i in seq_along(s)) {
j <- s[i]



# albums <-
# GET(
# paste0(
# 'https://api.spotify.com/v1/artists/',
# artistData()$artist_uri[j],
# '/albums'
# )
# ) %>% content

## Error: argument is of length zero

albums <-
GET(
paste0(
'https://api.spotify.com/v1/artists/',
artistData()$artist_uri[j],
'/albums'
), query = list(access_token = access_token)) %>% content


album_info <-  map_df(1:length(albums$items), function(x) {
tmp <- albums$items[[x]]

# Make sure the album_type is not "single"
if (tmp$album_type == 'album') {
data.frame(
album_uri = str_replace(tmp$uri, 'spotify:album:', ''),
album_name = str_replace_all(tmp$name, '\'', ''),
album_img = albums$items[[x]]$images[[1]]$url,
stringsAsFactors = F
) %>%
mutate(
album_release_date = GET(
paste0(
'https://api.spotify.com/v1/albums/',
str_replace(tmp$uri, 'spotify:album:', '')
), query = list(access_token = access_token))  %>%  # looks good
 content %>% .$release_date,
# you need a separate call to on "albums" to get release date.
album_release_year = ifelse(
nchar(album_release_date) == 4,
year(as.Date(album_release_date, '%Y')),
year(as.Date(album_release_date, '%Y-%m-%d'))
) # not all album_release_dates have months, so I created album_release year for sorting
)
} else {
NULL
}

}) %>% filter(!duplicated(tolower(album_name))) %>%  # Sometimes there are multiple versions (just with different capitalizations) of the same album
arrange(album_release_year) %>%
mutate(artist = artistData()$artist_name[j])



if (i != 1) {
allAlbum_info <- rbind(allAlbum_info, album_info)
} else {
allAlbum_info <- album_info
}
}

info = list(allAlbum_info = allAlbum_info)

print(glimpse(allAlbum_info))

return(info)

})






output$albumTable <- DT::renderDataTable({
allArtistsData()$allAlbum_info %>%
select(Select_Unwanted_Albums = album_name) %>%
DT::datatable(
class = 'compact stripe hover row-border order-column',



selection = "multiple",
rownames = FALSE,
options = list(
paging = TRUE,
searching = FALSE,
info = FALSE,
deferRender = TRUE,
scrollY = 200,
scroller = TRUE
)
)


})

DT::dataTableOutput("albumTable")
br()
actionButton(inputId = "goAlbums", label = "Get Tracks (takes seconds)")
  
```  

Column {data-width=350}
-----------------------------------------------------------------------

### Table

```{r}
# Get Tracks


tracksData <- eventReactive(input$goAlbums, {
selectedAlbums <- allArtistsData()$allAlbum_info$album_uri

# remove any albums not requireed in further processing
if (length(input$albumTable_rows_selected) > 0) {
t = as.integer(input$albumTable_rows_selected)
remove <- selectedAlbums[t]
selectedAlbums <- setdiff(selectedAlbums, remove)
}

track_info <- map_df(selectedAlbums, function(x) {
tracks <-
GET(paste0('https://api.spotify.com/v1/albums/', x, '/tracks'),query = list(access_token = access_token)) %>%
content %>%
.$items

uris <- map(1:length(tracks), function(z) {
gsub('spotify:track:', '', tracks[z][[1]]$uri)
}) %>% unlist %>% paste0(collapse = ',')

res <-
GET(
paste0('https://api.spotify.com/v1/audio-features/?ids=', uris),
query = list(access_token = access_token)
) %>% content %>% .$audio_features
df <- unlist(res) %>%
matrix(nrow = length(res), byrow = T) %>%
as.data.frame(stringsAsFactors = F)
names(df) <- names(res[[1]])
df <- df %>%
mutate(album_uri = x,
track_number = row_number()) %>%
rowwise %>%
mutate(track_name = tracks[[track_number]]$name) %>%
ungroup %>%
#left_join(album_info, by = 'album_uri') %>%
left_join(allArtistsData()$allAlbum_info, by = 'album_uri') %>%
rename(track_uri = id) %>%
select(-c(type, track_href, analysis_url, uri))
return(df) ## works to here
}) %>%
#  mutate(artist_img = artist_info$artist_img) %>%
#       mutate(artist_img =  artistData()$artist_img) %>% removed , 'artist_img' from list below - could add [1]?
mutate_at(
c(
'album_uri',
'track_uri',
'album_release_date',
'track_name',
'album_name'
),
funs(as.character)
) %>%
mutate_at(
c(
'danceability',
'energy',
'key',
'loudness',
'mode',
'speechiness',
'acousticness',
'album_release_year',
'instrumentalness',
'liveness',
'valence',
'tempo',
'duration_ms',
'time_signature',
'track_number'
),
funs(as.numeric(gsub(
'[^0-9.-]+', '', as.character(.)
)))
) # for some reason parse_number() from readr doesn't work here
write_csv(track_info, "beyonceTrackInfo.csv")
return(track_info)
})


output$tracksTable <- DT::renderDataTable({
tracksData() %>%
    mutate(minutes=round(duration_ms/60000,2)) %>% 
select(
artist,
album = album_name,
release = album_release_year,
track = track_name,
minutes,
danceability,
valence
)  %>%
DT::datatable(
class = 'compact stripe hover row-border order-column',


rownames = FALSE,
options = list(
paging = TRUE,
searching = TRUE,
info = FALSE,
pageLength = 5,
lengthMenu = c(5, 10, 15, 20)
)
)
})

DT::dataTableOutput("tracksTable")
```

### Chart Ordered by Date of Release (Valence is measure of positivity)

```{r, warning=FALSE}

radioButtons(inputId="metric",label=NULL,choices=c("Danceability","Instrumentalness","Valence","Track Order"), inline=TRUE)
hr()

 output$tracksPlot <- renderPlotly({
   # set for interactivity
   nms <- row.names(tracksData())
 
 #  choice <- "Danceability"
  
   if (input$metric== "Danceability") {
  p <-  tracksData() %>%
    filter(danceability>=0) %>% 
       plot_ly(x=~album_release_date,y=~danceability, key= nms, source = "tracks") %>% 
       add_markers(hoverinfo="text", name="The Artist",
              text=~paste0(album_name," - ",album_release_year,
                           "",track_name,
                           "",danceability))
   } else if (input$metric== "Instrumentalness") {
  p <-  tracksData() %>%
    filter(instrumentalness>=0) %>% 
       plot_ly(x=~album_release_date,y=~instrumentalness, key= nms, source = "tracks") %>% 
       add_markers(hoverinfo="text", name="The Artist",
              text=~paste0(album_name," - ",album_release_year,
                           "",track_name,
                           "",instrumentalness))
   } else if (input$metric== "Valence") {
  p <-  tracksData() %>%
    filter(valence>=0) %>% 
       plot_ly(x=~album_release_date,y=~valence, key= nms, source = "tracks") %>% 
       add_markers(hoverinfo="text", name="The Artist",
              text=~paste0(album_name," - ",album_release_year,
                           "",track_name,
                           "",valence))
   } else  {
  p <-  tracksData() %>%
        
       plot_ly(x=~album_release_date,y=~track_number, key= nms, source = "tracks") %>% 
       add_markers(hoverinfo="text", name="The Artist",
              text=~paste0(album_name," - ",album_release_year,
                           "",track_name))
   } 
   
  p %>% 
  layout(title=paste0(input$metric,"Hover for Info. Click for Video/Lyric"),
         xaxis=list(title="",showticklabels=FALSE),
         yaxis=list(title=input$metric))%>%
  config(displayModeBar = F, showLink = F)
})

plotlyOutput("tracksPlot")
 

```

Column {data-width=250}
-----------------------------------------------------------------------


### Video (1st on youtube - Could be a surprise e.g. may show Studio version even if Live requested or whole album)

```{r video, warning=FALSE}



output$video <- renderUI({
  
   req(event_data("plotly_click", source = "tracks"))
  # retrieve the clicked point, translate to artist/song combo
  d <- event_data("plotly_click", source = "tracks")
   s <- as.integer(d$key)

  song  <- paste(tracksData()$artist[s],tracksData()$track_name[s],sep="+")
  song  <- str_replace_all(song," ","+")
  song  <- str_split(song,"-")[[1]][1]
  
  #print(song)
 
 # navigate to correct index page on youtube and select first video link 
   ytURL <- paste0("https://www.youtube.com/results?search_query=",song)
 
   #print(ytURL)
 
 ytHTML <- read_html(ytURL)
 link <- ytHTML %>% # dpwnloads all the html
   html_nodes( "h3 a" ) %>%
   html_attr("href")
 
 #print(ytHTML)
 #print(link)

 

 if (length(link)>0) {
   theLink <- link[[1]]
 } else {
   theLink <- NA
 }
  theLink <- str_split(theLink,"=")[[1]][2]

  #print("thelink")
 # print(theLink)
  
  # using the vembedr package embed the resultant video
  embed_youtube(id = theLink,width = 450, query=list(autoplay=1))
  })

  uiOutput("video")


```

### Lyric - Google music version (contact them with corrections! Could be same title different lyric)

```{r}

output$lyric <- renderUI({
  
  req(event_data("plotly_click", source = "tracks"))
  # obtain song title from plot click
  d <- event_data("plotly_click", source = "tracks")
   s <- as.integer(d$key)
 
  song  <- tracksData()$track_name[s]
 

  
songUrl <- str_replace_all(song," ","%20")

#print(songUrl)

lyricURL <- paste0("https://www.google.ca/search?q=",songUrl,"%20lyrics&rct=j")

# scrapes appropriate url for all hrefs 
urls <- read_html(lyricURL) %>% 
  html_nodes("a") %>% 
  html_attr("href")

# print("urls")
# print(urls)

# Discover which is relevant url out of many
x <- which(map_lgl(urls, ~ str_sub(.x,1,29)=="https://play.google.com/music")==TRUE) 

# print("x")
# print(x)#26 which looks right (tho in fact a linsa lohan song)

# This attempts to cater for instances where title has an addition e.g "- Ramastered""
if (length(x)==0) {
  
  song  <- str_trim(str_split(song,"-")[[1]][1])

  
songUrl <- str_replace_all(song," ","%20")

lyricURL <- paste0("https://www.google.ca/search?q=",songUrl,"%20lyrics&rct=j")

urls <- read_html(lyricURL) %>% 
  html_nodes("a") %>% 
  html_attr("href")

x <- which(map_lgl(urls, ~ str_sub(.x,1,29)=="https://play.google.com/music")==TRUE)
  
}
# print("xagain")
# print(x)
# print("urls(x)")
# print(urls[x])
# print("b")

# now obtain the full lyric from Google Music - or print failure
if (length(x)>0) {
fullLyric <- urls[x]

outcome <-fullLyric %>% 
  read_html() %>% 
  html_nodes('p') %>% 
  paste(collapse = '\n')
} else {
  outcome <- "No Lyrics on Google Music available"
}

 
HTML(outcome)
  
})
br()
uiOutput("lyric")


```
