import streamlit as st import requests from transformers import pipeline # Enter your TMDb API key here TMDB_API_KEY = '2cc77b08a475b510f54a0dcca2c5c0c7' IMAGE_BASE_URL = "https://image.tmdb.org/t/p/w200" # TMDb base URL for poster images (width 200) @st.cache_resource def load_emotion_analyzer(): return pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base") emotion_analyzer = load_emotion_analyzer() def analyze_emotion(description): """Analyze emotion of a movie's description.""" emotions = emotion_analyzer(description) return emotions[0]['label'] # Return the top emotion def get_movie_genres(): """Fetches movie genres from TMDb API and returns a dictionary of genres.""" url = f'https://api.themoviedb.org/3/genre/movie/list?api_key={TMDB_API_KEY}&language=en-US' response = requests.get(url) if response.status_code == 200: genres = response.json().get('genres', []) return {genre['name']: genre['id'] for genre in genres} else: return None # Return None if the request failed def get_top_movies_by_genre(genre_id): """Fetches the top 10 highest-rated movies for a given genre ID from TMDb, sorted by title length.""" url = f'https://api.themoviedb.org/3/discover/movie?api_key={TMDB_API_KEY}&language=en-US&sort_by=vote_average.desc&vote_count.gte=1000&with_genres={genre_id}' response = requests.get(url) if response.status_code == 200: movies = response.json().get('results', [])[:10] # Get the top 10 results # Sort movies by title length, shortest to longest return sorted([(movie['title'], movie['vote_average'], movie['id'], movie['poster_path']) for movie in movies], key=lambda x: len(x[0])) else: return None def get_similar_movies(movie_id, genre_id): """Fetches similar movies for a given movie ID from TMDb and filters by the selected genre.""" url = f'https://api.themoviedb.org/3/movie/{movie_id}/similar?api_key={TMDB_API_KEY}&language=en-US' response = requests.get(url) if response.status_code == 200: movies = response.json().get('results', [])[:15] # Include the movie description (overview) in the returned values return [ (movie['title'], movie['vote_average'], movie['poster_path'], movie['id'], movie['overview']) for movie in movies if genre_id in movie['genre_ids'] and 'overview' in movie ] else: return None def get_movie_details(movie_id): """Fetches detailed information for a given movie ID from TMDb, including description, release date, director, and cast.""" url = f'https://api.themoviedb.org/3/movie/{movie_id}?api_key={TMDB_API_KEY}&language=en-US&append_to_response=credits' response = requests.get(url) if response.status_code == 200: movie = response.json() directors = [member['name'] for member in movie['credits']['crew'] if member['job'] == 'Director'] cast = [member['name'] for member in movie['credits']['cast'][:5]] # Get top 5 cast members return { "title": movie['title'], "rating": movie['vote_average'], "description": movie['overview'], "poster_path": movie['poster_path'], "release_date": movie['release_date'], "director": ", ".join(directors), "cast": ", ".join(cast) } else: return None # Initialize session state to store selected movies and recommendations if 'selected_movies' not in st.session_state: st.session_state['selected_movies'] = set() if 'recommendations' not in st.session_state: st.session_state['recommendations'] = [] if 'last_genre' not in st.session_state: st.session_state['last_genre'] = None # Streamlit app title st.title("Movie Recommender") # CSS for alignment and button styling st.markdown(""" """, unsafe_allow_html=True) # Fetch genres from TMDb and display in a dropdown genres = get_movie_genres() if genres is None: st.error("Failed to load movie genres. Please check your API key or connection.") else: genre_name = st.selectbox("Select a movie genre:", [""] + list(genres.keys())) # Include an empty option # Check if genre has changed, and reset selections if so if genre_name != st.session_state['last_genre']: st.session_state['selected_movies'].clear() st.session_state['recommendations'].clear() st.session_state['last_genre'] = genre_name # Only fetch and display movies if a genre is selected if genre_name: st.write("Please select at least one movie to generate recommendations.") genre_id = genres[genre_name] # Get the genre ID based on the selected genre name top_movies = get_top_movies_by_genre(genre_id) # Fetch top 10 movies in this genre, sorted by title length # Check if movies were successfully loaded if top_movies is None: st.error("Failed to load movies. Please check your API key or connection.") else: # Display the top 10 movies as clickable titles in a 5x2 grid layout with aligned rows st.write(f"Top 10 movies in the {genre_name} genre:") columns = st.columns(5) # Create 5 columns for each row # First row of 5 movies for index, (title, _, movie_id, poster_path) in enumerate(top_movies[:5]): col = columns[index] with col: st.markdown("
", unsafe_allow_html=True) # Display image st.image(f"{IMAGE_BASE_URL}{poster_path}", use_container_width=True) # Display clickable title as a button to toggle selection, with consistent height if st.button(f"{title} {'✅' if movie_id in st.session_state['selected_movies'] else ''}", key=f"title_{movie_id}"): if movie_id in st.session_state['selected_movies']: st.session_state['selected_movies'].remove(movie_id) else: st.session_state['selected_movies'].add(movie_id) st.markdown("
", unsafe_allow_html=True) # Second row of 5 movies for index, (title, _, movie_id, poster_path) in enumerate(top_movies[5:]): col = columns[index] with col: st.markdown("
", unsafe_allow_html=True) # Display image st.image(f"{IMAGE_BASE_URL}{poster_path}", use_container_width=True) # Display clickable title as a button to toggle selection, with consistent height if st.button(f"{title} {'✅' if movie_id in st.session_state['selected_movies'] else ''}", key=f"title_2_{movie_id}"): if movie_id in st.session_state['selected_movies']: st.session_state['selected_movies'].remove(movie_id) else: st.session_state['selected_movies'].add(movie_id) st.markdown("
", unsafe_allow_html=True) # Button to generate recommendations based on selected movies if st.button("Show Recommendations"): if st.session_state['selected_movies']: # Step 1: Collect emotions of user-selected movies with st.spinner("Analyzing emotions of selected movies..."): selected_emotions = [] for movie_id in st.session_state['selected_movies']: movie_details = get_movie_details(movie_id) if movie_details: emotion = analyze_emotion(movie_details["description"]) selected_emotions.append(emotion) # Step 2: Collect similar movies for all selected movies all_similar_movies = [] for movie_id in st.session_state['selected_movies']: similar_movies = get_similar_movies(movie_id, genre_id) if similar_movies: all_similar_movies.extend(similar_movies) # Step 3: Filter similar movies by matching emotions and remove duplicates recommendations = [] seen_movie_ids = set(st.session_state['selected_movies']) # Start with user-selected movies to exclude them with st.spinner("Filtering movies by description emotion..."): for title, rating, poster_path, movie_id, description in all_similar_movies: if movie_id not in seen_movie_ids: # Exclude already-seen movies movie_emotion = analyze_emotion(description) if movie_emotion in selected_emotions: seen_movie_ids.add(movie_id) # Mark this movie as seen to prevent duplicates recommendations.append((title, rating, poster_path, movie_id, movie_emotion)) # Store unique recommendations in session state st.session_state['recommendations'] = recommendations # Step 4: Display final recommendations if st.session_state['recommendations']: st.write("Based on your selections, you might also enjoy these top movies:") for title, rating, poster_path, movie_id, emotion in sorted(st.session_state['recommendations'], key=lambda x: x[1], reverse=True)[:5]: movie_details = get_movie_details(movie_id) if movie_details: col1, col2 = st.columns([1, 3]) with col1: st.image(f"{IMAGE_BASE_URL}{movie_details['poster_path']}", use_container_width=True) with col2: st.markdown(f"**Title:** {movie_details['title']}") st.markdown(f"**TMDb Rating:** {movie_details['rating']}") st.markdown(f"**Description:** {movie_details['description']}") # Additional Information button with overlay using expander with st.expander("Additional Information"): st.markdown(f"**Release Date:** {movie_details['release_date']}") st.markdown(f"**Director:** {movie_details['director']}") st.markdown(f"**Cast:** {movie_details['cast']}") else: st.warning("No movies matched your selected emotions.")