Spaces:
Sleeping
Sleeping
| # ============================== | |
| # 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) | |