# ============================== # Load Libraries # ============================== library(shiny) library(dplyr) library(shinycssloaders) library(shinyWidgets) library(recosystem) library(recommenderlab) # যদি প্রয়োজন হয় # ============================== # Load Data & Models # ============================== movies <- readRDS("movies.rds") ratings_filtered <- readRDS("ratings_filtered.rds") rating_matrix <- readRDS("rating_matrix.rds") r <- readRDS("svd_model.rds") ubcf_model <- readRDS("ubcf_model.rds") ibcf_model <- readRDS("ibcf_model.rds") topN_svd_list <- readRDS("topN_svd.rds") # Ensure poster_url exists if(!"poster_url" %in% colnames(movies)){ movies$poster_url <- "https://via.placeholder.com/150x225.png?text=No+Poster" } else { movies$poster_url <- ifelse(is.na(movies$poster_url), "https://via.placeholder.com/150x225.png?text=No+Poster", movies$poster_url) } # ============================== # Recommendation Function # ============================== recommend_movies <- function(user_id, N = 10, model_type = "SVD") { if(!(user_id %in% rownames(rating_matrix))) { return(data.frame(title="No recommendations available", poster_url="https://via.placeholder.com/150x225.png?text=No+Poster")) } if(model_type == "SVD") { user_movies <- ratings_filtered %>% filter(userId == user_id) %>% pull(movieId) unseen_movies <- setdiff(unique(ratings_filtered$movieId), user_movies) if(length(unseen_movies) == 0) { return(data.frame(title="No recommendations available", poster_url="https://via.placeholder.com/150x225.png?text=No+Poster")) } unseen_df <- data.frame(user = user_id, item = unseen_movies) pred_ratings <- r$predict(data_memory(user = unseen_df$user, item = unseen_df$item)) top_n_df <- data.frame(movieId = unseen_movies, pred_rating = pred_ratings) %>% arrange(desc(pred_rating)) %>% head(N) recs <- movies %>% filter(movieId %in% top_n_df$movieId) %>% select(title, poster_url) } else if(model_type == "UBCF") { pred <- predict(ubcf_model, rating_matrix[user_id,], type="topNList", n=N) ids <- as(pred, "list")[[1]] recs <- movies %>% filter(movieId %in% as.numeric(ids)) %>% select(title, poster_url) } else if(model_type == "IBCF") { pred <- predict(ibcf_model, rating_matrix[user_id,], type="topNList", n=N) ids <- as(pred, "list")[[1]] recs <- movies %>% filter(movieId %in% as.numeric(ids)) %>% select(title, poster_url) } else { stop("Invalid model_type. Choose 'SVD', 'UBCF', or 'IBCF'.") } if(nrow(recs) == 0) { recs <- data.frame(title="No recommendations available", poster_url="https://via.placeholder.com/150x225.png?text=No+Poster") } return(recs) } # ============================== # Shiny UI # ============================== ui <- fluidPage( tags$head( tags$style(HTML(" .header-emoji { font-size:34px; font-weight:bold; background: linear-gradient(90deg, #ff4e50, #f9d423); -webkit-background-clip:text; -webkit-text-fill-color:transparent; animation:gradient 3s ease infinite;} @keyframes gradient {0% {background-position:0%} 50% {background-position:100%} 100% {background-position:0%}} body { background-color: #fff0f5; } .btn-success { background: linear-gradient(45deg, #ff6b6b, #fbc531); color: #fff; font-weight:bold; transition:transform 0.2s; } .btn-success:hover { transform:scale(1.05); box-shadow:0px 4px 20px rgba(0,0,0,0.4); } .poster img { transition: transform 0.3s; border-radius:10px; } .poster img:hover { transform: scale(1.1) rotate(-2deg); box-shadow:0px 4px 20px rgba(0,0,0,0.5); } .title { text-align:center; font-size:12px; font-weight:bold; margin-top:5px; } .recommendation-scroll { max-height:600px; overflow-y:auto; } .tags-overlay { position:relative; } .tags-overlay span { position:absolute; top:5px; left:5px; font-size:16px; animation:sparkle 1.5s infinite; } @keyframes sparkle { 0%,100% {opacity:1;} 50% {opacity:0.3;} } ")) ), titlePanel(tags$div("🎥 MovieLens Recommendation System 🍿", class="header-emoji")), sidebarLayout( sidebarPanel( numericInput("user_id", "Enter User ID:", value = 1, min = 1, max = nrow(rating_matrix)), selectInput("model_type", "Select Model:", choices = c("SVD", "UBCF", "IBCF"), selected = "UBCF"), numericInput("top_n", "Number of Recommendations:", value = 10, min = 1, max = 20), actionBttn("recommend_btn", "GET RECOMMENDATIONS ✨", style="material-flat", color="success") ), mainPanel( h4("Recommended Movies 🎬:", style="color:darkblue;"), withSpinner(uiOutput("recommendations_ui"), type=6) ) ) ) # ============================== # Shiny Server # ============================== server <- function(input, output) { recommendations <- eventReactive(input$recommend_btn, { recommend_movies(user_id=input$user_id, N=input$top_n, model_type=input$model_type) }) output$recommendations_ui <- renderUI({ recs <- recommendations() div(class="recommendation-scroll", fluidRow( lapply(1:nrow(recs), function(i){ column(2, div(class="poster tags-overlay", tags$img(src=recs$poster_url[i], width="150px", height="225px"), tags$span("✨"), tags$p(recs$title[i], class="title") ) ) }) ) ) }) } # ============================== # Run Shiny App # ============================== shinyApp(ui = ui, server = server)