|
|
import pickle |
|
|
import streamlit as st |
|
|
import requests |
|
|
from datetime import datetime |
|
|
|
|
|
|
|
|
st.set_page_config(page_title="🎬 Movie Recommender", page_icon="🎞️", layout="wide") |
|
|
|
|
|
|
|
|
if 'name' not in st.session_state: |
|
|
st.session_state.name = None |
|
|
|
|
|
|
|
|
if st.session_state.name is None: |
|
|
|
|
|
st.title("🎬 Welcome to the Movie Recommender!") |
|
|
|
|
|
|
|
|
hour = datetime.now().hour |
|
|
greeting_time = "Good Morning ☀️" if hour < 12 else "Good Afternoon 🌤️" if hour < 18 else "Good Evening 🌙" |
|
|
st.markdown(f"### {greeting_time}") |
|
|
|
|
|
|
|
|
|
|
|
name = st.text_input("Enter your name", "") |
|
|
|
|
|
if st.button("Submit"): |
|
|
if name: |
|
|
st.session_state.name = name |
|
|
st.success(f"Hello, {name}! Welcome to the Movie Recommender 🎬") |
|
|
|
|
|
st.empty() |
|
|
else: |
|
|
st.error("Please enter a valid name.") |
|
|
|
|
|
|
|
|
else: |
|
|
|
|
|
st.markdown(""" |
|
|
<style> |
|
|
.sidebar .sidebar-content { |
|
|
background-color: #1c1c1c; /* خلفية داكنة جدًا */ |
|
|
color: white; |
|
|
padding: 15px; |
|
|
border-radius: 10px; |
|
|
} |
|
|
.sidebar .sidebar-content h1, .sidebar .sidebar-content h2 { |
|
|
font-size: 20px; |
|
|
font-weight: bold; |
|
|
color: #ecf0f1; /* لون النص */ |
|
|
} |
|
|
.sidebar .sidebar-content .stButton button { |
|
|
background-color: #3498db; /* زر لون أزرق */ |
|
|
color: white; |
|
|
font-weight: bold; |
|
|
border-radius: 5px; |
|
|
width: 100%; |
|
|
padding: 10px; |
|
|
} |
|
|
.sidebar .sidebar-content .stButton button:hover { |
|
|
background-color: #2980b9; |
|
|
} |
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
try: |
|
|
movies = pickle.load(open('movie_list.pkl', 'rb')) |
|
|
similarity = pickle.load(open('similarity.pkl', 'rb')) |
|
|
except FileNotFoundError: |
|
|
st.error("❌ Required data files (movie_list.pkl and similarity.pkl) not found.") |
|
|
st.stop() |
|
|
|
|
|
|
|
|
def fetch_poster(movie_id): |
|
|
try: |
|
|
url = f"https://api.themoviedb.org/3/movie/{movie_id}?api_key=8265bd1679663a7ea12ac168da84d2e8&language=en-US" |
|
|
response = requests.get(url) |
|
|
data = response.json() |
|
|
poster_path = data.get('poster_path') |
|
|
return f"https://image.tmdb.org/t/p/w500/{poster_path}" if poster_path else "https://via.placeholder.com/500x750?text=No+Image" |
|
|
except: |
|
|
return "https://via.placeholder.com/500x750?text=Error" |
|
|
|
|
|
def fetch_movie_details(movie_id): |
|
|
try: |
|
|
url = f"https://api.themoviedb.org/3/movie/{movie_id}?api_key=8265bd1679663a7ea12ac168da84d2e8&language=en-US" |
|
|
response = requests.get(url) |
|
|
data = response.json() |
|
|
overview = data.get('overview', 'No overview available.') |
|
|
rating = data.get('vote_average', 'No rating') |
|
|
release_date = data.get('release_date', 'No release date') |
|
|
genres = ', '.join([genre['name'] for genre in data.get('genres', [])]) |
|
|
return overview, rating, release_date, genres |
|
|
except: |
|
|
return "No details available.", "No rating", "No release date", "No genres" |
|
|
|
|
|
def recommend(movie): |
|
|
if movie not in movies['title'].values: |
|
|
return [], [], [] |
|
|
index = movies[movies['title'] == movie].index[0] |
|
|
distances = sorted(list(enumerate(similarity[index])), reverse=True, key=lambda x: x[1]) |
|
|
recommended_names = [] |
|
|
recommended_posters = [] |
|
|
recommended_ids = [] |
|
|
for i in distances[1:6]: |
|
|
movie_id = movies.iloc[i[0]].movie_id |
|
|
recommended_names.append(movies.iloc[i[0]].title) |
|
|
recommended_posters.append(fetch_poster(movie_id)) |
|
|
recommended_ids.append(movie_id) |
|
|
return recommended_names, recommended_posters, recommended_ids |
|
|
|
|
|
def fetch_popular_movies(): |
|
|
url = "https://api.themoviedb.org/3/movie/popular?api_key=8265bd1679663a7ea12ac168da84d2e8&language=en-US&page=1" |
|
|
try: |
|
|
response = requests.get(url) |
|
|
data = response.json() |
|
|
popular_movies = [] |
|
|
for movie in data['results'][:60]: |
|
|
title = movie['title'] |
|
|
poster_path = movie.get('poster_path') |
|
|
poster_url = f"https://image.tmdb.org/t/p/w500/{poster_path}" if poster_path else "https://via.placeholder.com/500x750?text=No+Image" |
|
|
movie_id = movie['id'] |
|
|
popular_movies.append((title, poster_url, movie_id)) |
|
|
return popular_movies |
|
|
except: |
|
|
return [] |
|
|
|
|
|
|
|
|
st.sidebar.markdown(""" |
|
|
<div style="text-align:center; color:white; font-size:30px; font-weight:bold;"> |
|
|
🎞️ Movie Recommender |
|
|
</div> |
|
|
<hr style="border-color: #ecf0f1;"> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.session_state.selected_section = st.sidebar.radio( |
|
|
"Navigate", |
|
|
["Popular Movies", "Search & Recommend Movies", "Movie Details"] |
|
|
) |
|
|
|
|
|
|
|
|
st.markdown(f"### Hello, {st.session_state.get('name', 'Guest')} 👋", unsafe_allow_html=True) |
|
|
|
|
|
if st.session_state.selected_section == "Popular Movies": |
|
|
with st.expander("Popular Movies", expanded=True): |
|
|
popular = fetch_popular_movies() |
|
|
if popular: |
|
|
cols = st.columns(5) |
|
|
for i, (title, poster_url, movie_id) in enumerate(popular): |
|
|
with cols[i % 5]: |
|
|
st.image(poster_url, use_container_width=True) |
|
|
st.markdown(f"<div class='movie-title'>{title}</div>", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
if f"show_details_{i}" not in st.session_state: |
|
|
st.session_state[f"show_details_{i}"] = False |
|
|
|
|
|
|
|
|
if st.button(" Details 📄", key=f"popular_details_{i}"): |
|
|
|
|
|
st.session_state[f"show_details_{i}"] = not st.session_state[f"show_details_{i}"] |
|
|
|
|
|
|
|
|
if st.session_state[f"show_details_{i}"]: |
|
|
overview, rating, release_date, genres = fetch_movie_details(movie_id) |
|
|
st.markdown(f"**📅 Release Date:** {release_date}") |
|
|
st.markdown(f"**⭐ Rating:** {rating}") |
|
|
st.markdown(f"**🎭 Genres:** {genres}") |
|
|
st.markdown(f"**📝 Overview:** {overview}") |
|
|
else: |
|
|
st.markdown("**Details Closed**") |
|
|
|
|
|
else: |
|
|
st.warning("Could not fetch popular movies.") |
|
|
|
|
|
elif st.session_state.selected_section == "Search & Recommend Movies": |
|
|
movie_list = movies['title'].values |
|
|
selected_movie = st.selectbox("🎥 Select a Movie", movie_list) |
|
|
rating = st.slider("⭐ Your Rating", 1, 10, 1) |
|
|
st.write(f"✅ Your rating: {rating} stars") |
|
|
|
|
|
if "show_recommendations" not in st.session_state: |
|
|
st.session_state.show_recommendations = False |
|
|
|
|
|
if st.button("🎯 Show 5 Recommendations"): |
|
|
st.session_state.show_recommendations = not st.session_state.show_recommendations |
|
|
|
|
|
if st.session_state.show_recommendations: |
|
|
with st.spinner("⏳ Fetching recommendations..."): |
|
|
names, posters, ids = recommend(selected_movie) |
|
|
if names: |
|
|
st.markdown(f"### ✅ Recommended Movies Similar to {selected_movie}:") |
|
|
cols = st.columns(5) |
|
|
for i in range(len(names)): |
|
|
with cols[i % 5]: |
|
|
st.image(posters[i], use_container_width=True) |
|
|
st.markdown(f"<div class='movie-title'>{names[i]}</div>", unsafe_allow_html=True) |
|
|
if st.button("📄 Details", key=f"recommend_details_{i}"): |
|
|
overview, rating, release_date, genres = fetch_movie_details(ids[i]) |
|
|
st.markdown(f"**📅 Release Date:** {release_date}") |
|
|
st.markdown(f"**⭐ Rating:** {rating}") |
|
|
st.markdown(f"**🎭 Genres:** {genres}") |
|
|
st.markdown(f"**📝 Overview:** {overview}") |
|
|
else: |
|
|
st.warning("No recommendations found.") |
|
|
|
|
|
elif st.session_state.selected_section == "Movie Details": |
|
|
if "show_details" not in st.session_state: |
|
|
st.session_state.show_details = {} |
|
|
|
|
|
movie_list = movies['title'].values |
|
|
selected_movie = st.selectbox("🎥 Select a Movie to View Details", movie_list) |
|
|
index = movies[movies['title'] == selected_movie].index[0] |
|
|
movie_id = movies.iloc[index].movie_id |
|
|
overview, rating, release_date, genres = fetch_movie_details(movie_id) |
|
|
|
|
|
|
|
|
if selected_movie not in st.session_state.show_details: |
|
|
st.session_state.show_details[selected_movie] = True |
|
|
|
|
|
if st.button(f"Show/Hide Details for {selected_movie} 📄"): |
|
|
st.session_state.show_details[selected_movie] = not st.session_state.show_details[selected_movie] |
|
|
|
|
|
|
|
|
if st.session_state.show_details[selected_movie]: |
|
|
st.image(fetch_poster(movie_id), use_container_width=True) |
|
|
st.markdown(f"**📅 Release Date:** {release_date}") |
|
|
st.markdown(f"**⭐ Rating:** {rating}") |
|
|
st.markdown(f"**🎭 Genres:** {genres}") |
|
|
st.markdown(f"**📝 Overview:** {overview}") |
|
|
else: |
|
|
st.markdown(f"Details for {selected_movie} are hidden. Click the button to view them.") |
|
|
|
|
|
|
|
|
st.markdown("<hr>", unsafe_allow_html=True) |
|
|
st.markdown("<div class='footer'>© 2025 • by Youssef samy • Powered by Youssef Samy API</div>", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|