Zachary Lorico Hertz
  • About
  • Research
  • Teaching
  • Blog
  • C.V.
  • Projects
#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 700

## file: app.R
# Big Thief Lyric Bot

library(shiny)
library(shinythemes)
library(dplyr)

# UI
ui <- fluidPage(
  theme = shinytheme("flatly"),
  
  tags$head(
    tags$style(HTML("
      .main-container {
        max-width: 600px;
        margin: 20px auto;
        text-align: center;
      }
      
      .lyrics-display {
        background: #f8f9fa;
        border-left: 4px solid #007bff;
        padding: 30px;
        margin: 30px 0;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
      }
      
      .lyrics-text {
        font-size: 18px;
        line-height: 1.6;
        color: #2c3e50;
        font-style: italic;
        white-space: pre-line;
        margin-bottom: 20px;
      }
      
      .attribution {
        font-size: 14px;
        color: #6c757d;
        font-weight: 500;
      }
      
      .btn-generate {
        font-size: 16px;
        padding: 12px 30px;
        margin: 20px 0;
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        border: none;
        border-radius: 25px;
        color: white;
        transition: all 0.3s ease;
      }
      
      .btn-generate:hover {
        transform: translateY(-2px);
        box-shadow: 0 5px 15px rgba(0,0,0,0.2);
        background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
      }
      
      .stats {
        font-size: 12px;
        color: #868e96;
        margin-top: 20px;
      }
      
      .loading {
        color: #6c757d;
        font-style: italic;
      }
    "))
  ),
  
  div(class = "main-container",
    h1("Big Thief Lyric Generator"),
    p("This is a webapp I coded that randomly displays snippets from Big Thief's discography."),
    
    actionButton("generate", "✨ View Random Lyrics", class = "btn btn-generate"),
    
    uiOutput("lyrics_display"),
    
    div(class = "stats",
      textOutput("stats_text")
    )
  )
)

# Server
server <- function(input, output, session) {
  # Reactive value to store the loaded data
  lyric_chunks <- reactiveVal(NULL)
  data_loaded <- reactiveVal(FALSE)
  
  # Load data when app starts
  observe({
    tryCatch({
      data_url <- "https://zacharylhertz.github.io/files/chunks.csv"
      temp_file <- tempfile(fileext = ".csv")
      download.file(data_url, temp_file, mode = "wb")
      chunks_data <- read.csv(temp_file, stringsAsFactors = FALSE)
      lyric_chunks(chunks_data)
      data_loaded(TRUE)
      unlink(temp_file)
    }, error = function(e) {
      showNotification("Failed to load lyric data. Please check your internet connection.",
                       type = "error", duration = 5)
    })
  })
  
  # Random lyric function - now uses reactive data
  get_random_lyric <- function() {
    chunks_data <- lyric_chunks()
    if (is.null(chunks_data) || nrow(chunks_data) == 0) {
      return(list(
        lyrics = "Unable to load lyrics data",
        attribution = "Please refresh the page"
      ))
    }
    
    sample_chunk <- chunks_data %>% slice_sample(n = 1)
    lyrics_text <- sample_chunk$lyrics_chunk
    
    # Create title with hyperlink if URL exists and is non-empty
    if (!is.null(sample_chunk$url) && !is.na(sample_chunk$url) && sample_chunk$url != "") {
      title_html <- paste0('<a href="', sample_chunk$url, '" target="_blank" style="color: inherit; text-decoration: underline;">', 
                           sample_chunk$title, '</a>')
    } else {
      title_html <- sample_chunk$title
    }
    
    attribution <- paste0("— ", title_html, " by ", sample_chunk$artist, " (", sample_chunk$year, ")")
    return(list(lyrics = lyrics_text, attribution = attribution))
  }
  
  # Reactive values to store current lyrics
  current_lyrics <- reactiveVal("🎵 Click the button above to start generating lyrics.")
  current_attribution <- reactiveVal("— Ready to explore Adrianne Lenker's penmanship?")
  
  # Update when button is clicked
  observeEvent(input$generate, {
    if (data_loaded()) {
      result <- get_random_lyric()
      current_lyrics(paste("🎵", result$lyrics, "🎵"))
      current_attribution(result$attribution)
    } else {
      showNotification("Data is still loading, please wait...", type = "warning")
    }
  })
  
  # Render the lyrics display
  output$lyrics_display <- renderUI({
    div(class = "lyrics-display",
      div(id = "lyrics-output",
        div(class = "lyrics-text", current_lyrics()),
        div(class = "attribution", HTML(current_attribution()))
      )
    )
  })
  
  # Stats text
  output$stats_text <- renderText({
    if (data_loaded() && !is.null(lyric_chunks())) {
      paste("Built from", nrow(lyric_chunks()), "lyrical chunks across Big Thief's discography.")
    } else {
      "Loading lyric data..."
    }
  })
}

# Create the app
shinyApp(ui = ui, server = server)

Copyright 2025, Zachary Lorico Hertz.
Built adopting code in part from Silvia Canelón, Carlisle Rainey, and Sam Shanny-Csik.