USER
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register - Emotion Music Recommender</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
<style>
body {
background: #f8f9fa;
padding-top: 50px;
}
.register-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
background: white;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.alert {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="register-container">
<h2 class="text-center mb-4">Register</h2>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('register') }}">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</form>
<div class="text-center mt-3">
<p>Already have an account? <a href="{{ url_for('login') }}">Login</a></p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
this is reggister page
this is login page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Emotion Music Recommender</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
<style>
body {
background: #f8f9fa;
padding-top: 50px;
}
.login-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
background: white;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.alert {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="login-container">
<h2 class="text-center mb-4">Login</h2>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('login') }}">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Login</button>
</div>
</form>
<div class="text-center mt-3">
<p>Don't have an account? <a href="{{ url_for('register') }}">Register</a></p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
this is app.py
import os
import numpy as np
import cv2
import dlib
import pandas as pd
import tensorflow as tf
import ast # For safely converting strings to lists
from flask import Flask, render_template, Response, request, redirect, url_for, session, flash
from tensorflow.keras.models import load_model
import base64
from io import BytesIO
from PIL import Image
from functools import wraps
from werkzeug.security import generate_password_hash, check_password_hash
import sqlite3
app = Flask(__name__)
app.secret_key = 'your_very_secret_key_here' # Required for session management
os.environ["CUDA_VISIBLE_DEVICES"] = "-1" # Disable GPU if needed
# Load Pretrained Model and Labels
model = load_model("models/model.h5", compile=False)
labels = np.load("models/labels.npy")
# Load Dlib's Face Detector & Landmark Predictor
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("models/shape_predictor_68_face_landmarks.dat")
# Load Music Recommendations from CSV
music_df = pd.read_csv("music_recommendations.csv")
# Database initialization
def init_db():
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# Create users table
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
role TEXT NOT NULL
)
''')
# Create default admin user if not exists
cursor.execute("SELECT * FROM users WHERE username = 'admin'")
if cursor.fetchone() is None:
admin_password = generate_password_hash("admin123")
cursor.execute("INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, ?)",
("admin", admin_password, "admin@example.com", "admin"))
conn.commit()
conn.close()
# Call database initialization
init_db()
# Authentication decorators
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
flash('Please log in to access this page', 'warning')
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
def admin_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
flash('Please log in to access this page', 'warning')
return redirect(url_for('login'))
elif session.get('role') != 'admin':
flash('You need admin privileges to access this page', 'danger')
return redirect(url_for('home'))
return f(*args, **kwargs)
return decorated_function
def get_music_recommendation(emotion):
"""Fetch recommended music based on detected emotion from CSV."""
# Convert 'seeds' column to lists safely
music_df["seeds"] = music_df["seeds"].apply(
lambda x: ast.literal_eval(x) if isinstance(x, str) and x.startswith("[") else x
)
# Filter songs where the detected emotion is in the 'seeds' list
filtered = music_df[music_df["seeds"].apply(lambda seeds: emotion.lower() in [s.lower() for s in seeds])]
if not filtered.empty:
return filtered["lastfm_url"].tolist() # Return a list of matching URLs
else:
return ["No recommendation found for this emotion."]
def process_landmarks(landmarks, reference_point=None):
"""Convert landmarks into a relative coordinate vector."""
lst = []
for lm in landmarks:
lst.append(lm[0] - reference_point[0] if reference_point else lm[0])
lst.append(lm[1] - reference_point[1] if reference_point else lm[1])
return lst
def predict_emotion(image):
"""Detect face landmarks and predict emotion from image array."""
if image is None:
return "Error: Image not found."
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = detector(gray)
if len(faces) == 0:
return "No face detected."
for face in faces:
landmarks = predictor(gray, face)
landmarks = [(p.x, p.y) for p in landmarks.parts()]
face_landmarks = process_landmarks(landmarks, landmarks[30]) # Reference: Nose tip (index 30)
feature_vector = np.array(face_landmarks, dtype=np.float32)
feature_vector = np.pad(feature_vector, (0, max(0, 1020 - len(feature_vector))), mode='constant')[:1020]
feature_vector = feature_vector.reshape(1, -1)
pred = model.predict(feature_vector)
emotion_label = labels[np.argmax(pred)]
return emotion_label
def predict_emotion_from_file(image_path):
"""Detect face landmarks and predict emotion from file path."""
image = cv2.imread(image_path)
return predict_emotion(image)
# Authentication Routes
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute("SELECT id, password, role FROM users WHERE username = ?", (username,))
user = cursor.fetchone()
conn.close()
if user and check_password_hash(user[1], password):
session['user_id'] = user[0]
session['username'] = username
session['role'] = user[2]
flash(f'Welcome back, {username}!', 'success')
return redirect(url_for('home'))
else:
flash('Invalid username or password', 'danger')
return render_template('login.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
email = request.form['email']
role = 'user' # Default role is user
# Check if it's the first user, make them admin
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM users")
if cursor.fetchone()[0] == 0:
role = 'admin'
# Check if username or email already exists
cursor.execute("SELECT id FROM users WHERE username = ? OR email = ?", (username, email))
if cursor.fetchone():
conn.close()
flash('Username or email already exists', 'danger')
return redirect(url_for('register'))
# Hash password and save user
hashed_password = generate_password_hash(password)
cursor.execute("INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, ?)",
(username, hashed_password, email, role))
conn.commit()
conn.close()
flash('Registration successful! You can now log in.', 'success')
return redirect(url_for('login'))
return render_template('register.html')
@app.route('/logout')
def logout():
session.clear()
flash('You have been logged out', 'info')
return redirect(url_for('login'))
# Admin Routes
@app.route('/admin')
@admin_required
def admin_dashboard():
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute("SELECT id, username, email, role FROM users")
users = cursor.fetchall()
conn.close()
return render_template('admin_dashboard.html', users=users)
@app.route('/admin/delete_user/<int:user_id>', methods=['POST'])
@admin_required
def delete_user(user_id):
# Don't allow admins to delete themselves
if user_id == session.get('user_id'):
flash('You cannot delete your own account while logged in', 'danger')
return redirect(url_for('admin_dashboard'))
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute("DELETE FROM users WHERE id = ?", (user_id,))
conn.commit()
conn.close()
flash('User has been deleted', 'success')
return redirect(url_for('admin_dashboard'))
@app.route('/admin/edit_user/<int:user_id>', methods=['GET', 'POST'])
@admin_required
def edit_user(user_id):
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
if request.method == 'POST':
username = request.form['username']
email = request.form['email']
role = request.form['role']
# Update user info
cursor.execute("UPDATE users SET username = ?, email = ?, role = ? WHERE id = ?",
(username, email, role, user_id))
# If password field is not empty, update password
if request.form['password']:
hashed_password = generate_password_hash(request.form['password'])
cursor.execute("UPDATE users SET password = ? WHERE id = ?", (hashed_password, user_id))
conn.commit()
flash('User updated successfully', 'success')
return redirect(url_for('admin_dashboard'))
# Get user data
cursor.execute("SELECT id, username, email, role FROM users WHERE id = ?", (user_id,))
user = cursor.fetchone()
conn.close()
if not user:
flash('User not found', 'danger')
return redirect(url_for('admin_dashboard'))
return render_template('edit_user.html', user=user)
# Main Application Routes
@app.route('/')
def index():
# Redirect to login if not authenticated
if 'user_id' not in session:
return redirect(url_for('login'))
return redirect(url_for('home'))
@app.route('/home')
@login_required
def home():
return render_template('index.html', username=session.get('username'))
@app.route('/recommend_music', methods=['POST'])
@login_required
def handle_music_request():
if 'file' not in request.files:
return render_template('index.html', error="No file selected", username=session.get('username'))
file = request.files['file']
if file.filename == '':
return render_template('index.html', error="No file selected", username=session.get('username'))
file_path = os.path.join("static/uploads", file.filename)
os.makedirs("static/uploads", exist_ok=True)
file.save(file_path)
detected_emotion = predict_emotion_from_file(file_path)
if "Error" in detected_emotion or "No face" in detected_emotion:
return render_template('index.html', error=detected_emotion, username=session.get('username'))
music_links = get_music_recommendation(detected_emotion) # Returns a list of music links
return render_template('index.html', detected_emotion=detected_emotion, music_links=music_links, username=session.get('username'))
@app.route('/webcam')
@login_required
def webcam_page():
"""Render the webcam capture page."""
return render_template('webcam.html', username=session.get('username'))
@app.route('/capture', methods=['POST'])
@login_required
def capture():
"""Process the captured image from webcam."""
image_data = request.form['image_data']
# Remove the prefix 'data:image/jpeg;base64,'
image_data = image_data.split(',')[1]
# Convert base64 to image
image_bytes = base64.b64decode(image_data)
image = Image.open(BytesIO(image_bytes))
# Convert PIL Image to OpenCV format
open_cv_image = np.array(image)
open_cv_image = open_cv_image[:, :, ::-1].copy() # Convert RGB to BGR
# Process image for emotion detection
detected_emotion = predict_emotion(open_cv_image)
if "Error" in detected_emotion or "No face" in detected_emotion:
return {"error": detected_emotion}
# Get music recommendations
music_links = get_music_recommendation(detected_emotion)
return {"emotion": detected_emotion, "music_links": music_links}
# User profile routes
@app.route('/profile')
@login_required
def profile():
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute("SELECT username, email FROM users WHERE id = ?", (session.get('user_id'),))
user = cursor.fetchone()
conn.close()
return render_template('profile.html', username=user[0], email=user[1])
@app.route('/update_profile', methods=['POST'])
@login_required
def update_profile():
username = request.form['username']
email = request.form['email']
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# Check if username or email is already taken by another user
cursor.execute("SELECT id FROM users WHERE (username = ? OR email = ?) AND id != ?",
(username, email, session.get('user_id')))
if cursor.fetchone():
conn.close()
flash('Username or email already in use', 'danger')
return redirect(url_for('profile'))
# Update user information
cursor.execute("UPDATE users SET username = ?, email = ? WHERE id = ?",
(username, email, session.get('user_id')))
# Update password if provided
if request.form['password'] and request.form['password'].strip():
password = generate_password_hash(request.form['password'])
cursor.execute("UPDATE users SET password = ? WHERE id = ?",
(password, session.get('user_id')))
conn.commit()
conn.close()
# Update session with new username
session['username'] = username
flash('Profile updated successfully', 'success')
return redirect(url_for('profile'))
# Live Video Feed Route
def generate_frames():
"""Generate frames from webcam."""
cap = cv2.VideoCapture(0)
while True:
success, frame = cap.read()
if not success:
break
_, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
@app.route('/video_feed')
@login_required
def video_feed():
"""Video streaming route."""
return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
# Make sure uploads directory exists
os.makedirs("static/uploads", exist_ok=True)
app.run(debug=True, use_reloader=False)
this is index.htm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EmoSense - Emotion-Based Music</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--primary: #6c63ff;
--secondary: #4e57ef;
--accent: #ff6b6b;
--light: #f5f7ff;
--dark: #2a2a72;
--gradient-start: #6c63ff;
--gradient-end: #7e24fa;
}
body {
background: linear-gradient(135deg, var(--light) 0%, #e0e6ff 100%);
font-family: 'Poppins', sans-serif;
min-height: 100vh;
color: #333;
}
/* Navbar Styling */
.navbar {
background: linear-gradient(90deg, var(--dark) 0%, #3f3d9d 100%);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 15px 0;
}
.navbar-brand {
font-weight: 700;
font-size: 26px;
color: white;
display: flex;
align-items: center;
}
.logo-icon {
margin-right: 10px;
font-size: 28px;
background: -webkit-linear-gradient(var(--accent), var(--primary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.logo-text span {
background: -webkit-linear-gradient(var(--accent), var(--primary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.nav-link {
font-weight: 500;
position: relative;
margin: 0 10px;
transition: all 0.3s ease;
}
.nav-link::after {
content: '';
position: absolute;
width: 0;
height: 2px;
background: var(--accent);
bottom: -5px;
left: 0;
transition: width 0.3s;
}
.nav-link:hover::after,
.nav-link.active::after {
width: 100%;
}
/* Main Container */
.main-container {
margin-top: 50px;
margin-bottom: 70px;
}
/* Hero Section */
.hero-section {
padding: 30px 0;
text-align: center;
position: relative;
}
.hero-section h1 {
font-size: 3rem;
font-weight: 700;
margin-bottom: 20px;
background: -webkit-linear-gradient(var(--dark), var(--primary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hero-section .lead {
font-size: 1.2rem;
color: #555;
max-width: 700px;
margin: 0 auto 30px;
}
.hero-blob {
position: absolute;
width: 600px;
height: 600px;
z-index: -1;
opacity: 0.1;
top: -300px;
right: -300px;
border-radius: 50%;
background: linear-gradient(45deg, var(--primary), var(--accent));
filter: blur(60px);
}
/* Feature Cards */
.card {
border: none;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
overflow: hidden;
transition: all 0.3s ease;
height: 100%;
}
.card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 35px rgba(108, 99, 255, 0.2);
}
.feature-card {
padding: 40px 30px;
text-align: center;
border-radius: 20px;
background: white;
border-bottom: 5px solid transparent;
transition: all 0.3s;
}
.feature-card:hover {
border-bottom: 5px solid var(--primary);
}
.feature-icon-wrap {
width: 90px;
height: 90px;
border-radius: 50%;
background: linear-gradient(135deg, rgba(108, 99, 255, 0.1) 0%, rgba(108, 99, 255, 0.2) 100%);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 25px;
}
.feature-icon {
font-size: 40px;
background: -webkit-linear-gradient(var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.feature-title {
font-weight: 600;
margin-bottom: 15px;
font-size: 22px;
color: var(--dark);
}
.feature-text {
color: #666;
font-size: 16px;
line-height: 1.6;
}
/* Card Headers */
.card-header {
background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
font-weight: 600;
padding: 20px;
border: none;
text-align: center;
font-size: 22px;
}
/* Card Body */
.card-body {
padding: 30px;
background-color: white;
}
/* Emotion Icon */
.emotion-icon {
font-size: 60px;
margin-bottom: 25px;
background: -webkit-linear-gradient(var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-align: center;
display: block;
}
/* Upload Area */
.upload-area {
border: 2px dashed var(--primary);
padding: 35px;
text-align: center;
border-radius: 15px;
margin-bottom: 25px;
background-color: rgba(108, 99, 255, 0.05);
transition: all 0.3s;
}
.upload-area:hover {
background-color: rgba(108, 99, 255, 0.1);
}
.upload-icon {
font-size: 50px;
margin-bottom: 15px;
color: var(--primary);
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
/* Buttons */
.btn-primary {
background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
border: none;
padding: 12px 25px;
font-weight: 600;
border-radius: 10px;
transition: all 0.3s;
position: relative;
overflow: hidden;
z-index: 1;
}
.btn-primary::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.2) 50%, transparent 100%);
transition: left 0.7s ease;
z-index: -1;
}
.btn-primary:hover::before {
left: 100%;
}
.btn-primary:hover {
box-shadow: 0 5px 15px rgba(108, 99, 255, 0.4);
transform: translateY(-3px);
}
/* Results Area */
.results-area {
background-color: var(--light);
padding: 25px;
border-radius: 15px;
margin-top: 30px;
box-shadow: 0 5px 20px rgba(0,0,0,0.05);
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Song List */
.song-list {
margin-top: 15px;
}
.song-item {
margin-bottom: 12px;
padding: 15px;
border-radius: 10px;
background-color: white;
box-shadow: 0 3px 10px rgba(0,0,0,0.05);
transition: all 0.2s;
border-left: 4px solid transparent;
}
.song-item:hover {
background-color: #f0f2ff;
border-left: 4px solid var(--primary);
transform: translateX(5px);
}
.song-link {
color: var(--dark);
text-decoration: none;
display: flex;
align-items: center;
}
.song-link i {
margin-right: 12px;
color: var(--primary);
font-size: 18px;
transition: transform 0.3s;
}
.song-item:hover .song-link i {
transform: scale(1.2);
}
/* Emotion Badge */
.emotion-badge {
display: inline-block;
padding: 8px 20px;
background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
border-radius: 30px;
font-weight: 600;
margin-top: 10px;
box-shadow: 0 4px 10px rgba(108, 99, 255, 0.3);
}
/* Webcam Container */
.webcam-card {
background: white;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
.webcam-card-body {
padding: 30px;
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.webcam-button {
margin-top: 20px;
min-width: 200px;
display: flex;
align-items: center;
justify-content: center;
}
.webcam-button i {
margin-right: 10px;
font-size: 18px;
}
.webcam-text {
font-size: 18px;
margin-bottom: 30px;
text-align: center;
color: #555;
}
.webcam-icon {
font-size: 70px;
margin-bottom: 20px;
background: -webkit-linear-gradient(var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
/* How It Works Section */
.section-title {
color: var(--dark);
font-weight: 700;
margin-bottom: 40px;
position: relative;
display: inline-block;
font-size: 2.5rem;
}
.section-title:after {
content: '';
position: absolute;
bottom: -15px;
left: 0;
width: 60px;
height: 4px;
background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
border-radius: 2px;
}
.step-card {
padding: 30px;
border-radius: 20px;
text-align: center;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
height: 100%;
position: relative;
overflow: hidden;
z-index: 1;
}
.step-number {
position: absolute;
top: 10px;
right: 15px;
font-size: 60px;
font-weight: 700;
opacity: 0.1;
z-index: -1;
}
.step-icon-circle {
background: linear-gradient(135deg, rgba(108, 99, 255, 0.1) 0%, rgba(108, 99, 255, 0.2) 100%);
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto 20px;
}
.step-icon {
font-size: 35px;
color: var(--primary);
}
.step-title {
font-weight: 600;
margin-bottom: 15px;
font-size: 20px;
color: var(--dark);
}
/* Footer */
footer {
background: linear-gradient(90deg, var(--dark) 0%, #3f3d9d 100%);
color: white;
padding: 30px 0;
text-align: center;
position: relative;
overflow: hidden;
}
.footer-blob {
position: absolute;
width: 400px;
height: 400px;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
border-radius: 50%;
top: -200px;
left: -200px;
}
.footer-blob2 {
position: absolute;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
border-radius: 50%;
bottom: -150px;
right: -150px;
}
.footer-content {
position: relative;
z-index: 1;
}
.footer-links {
margin-bottom: 20px;
}
.footer-links a {
color: white;
margin: 0 15px;
text-decoration: none;
font-weight: 500;
transition: all 0.3s;
}
.footer-links a:hover {
color: var(--accent);
}
.footer-social {
margin-bottom: 20px;
}
.social-icon {
display: inline-flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
background: rgba(255,255,255,0.1);
border-radius: 50%;
margin: 0 10px;
color: white;
font-size: 18px;
transition: all 0.3s;
}
.social-icon:hover {
background: var(--accent);
transform: translateY(-5px);
}
/* Media Queries */
@media (max-width: 768px) {
.hero-section h1 {
font-size: 2.2rem;
}
.feature-card {
padding: 30px 20px;
margin-bottom: 20px;
}
.webcam-card {
margin-top: 30px;
}
}
</style>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
<div class="container">
<a class="navbar-brand logo-text" href="/">
<i class="fas fa-music-alt logo-icon"></i>
Emo<span>Sense</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link active" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#how-it-works">How It Works</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('webcam_page') }}">Webcam</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#about">About</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Hero Section -->
<div class="hero-section">
<div class="hero-blob"></div>
<div class="container">
<div class="row">
<div class="col-lg-10 mx-auto text-center">
<h1 class="mb-3">Discover Music That Matches Your Mood</h1>
<p class="lead mb-4">EmoSense uses advanced facial emotion recognition to recommend music that perfectly complements how you feel.</p>
</div>
</div>
</div>
</div>
<!-- Main Container -->
<div class="container main-container">
<!-- Features Section -->
<div class="row mb-5">
<div class="col-md-4 mb-4">
<div class="feature-card h-100">
<div class="feature-icon-wrap">
<i class="fas fa-camera feature-icon"></i>
</div>
<h3 class="feature-title">Upload Photo</h3>
<p class="feature-text">Upload a selfie or any facial photo to analyze your emotion and get personalized music recommendations.</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card h-100">
<div class="feature-icon-wrap">
<i class="fas fa-brain feature-icon"></i>
</div>
<h3 class="feature-title">Emotion Analysis</h3>
<p class="feature-text">Our advanced AI detects your emotional state from facial expressions with high accuracy.</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card h-100">
<div class="feature-icon-wrap">
<i class="fas fa-music feature-icon"></i>
</div>
<h3 class="feature-title">Music Match</h3>
<p class="feature-text">Get personalized music recommendations based on your emotion to enhance your mood.</p>
</div>
</div>
</div>
<div class="row mt-5">
<!-- Image Upload Section -->
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-header">
<i class="fas fa-image me-2"></i> Photo Emotion Analysis
</div>
<div class="card-body">
<i class="fas fa-smile emotion-icon"></i>
<form action="/recommend_music" method="post" enctype="multipart/form-data">
<div class="upload-area">
<i class="fas fa-cloud-upload-alt upload-icon"></i>
<h4>Upload Your Photo</h4>
<p>We'll analyze your facial expression to detect emotions</p>
<input type="file" name="file" class="form-control mt-3" accept="image/*">
</div>
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-magic me-2"></i> Get Music Recommendations
</button>
</form>
<!-- Results Display -->
{% if detected_emotion %}
<div class="results-area">
<h4>Results:</h4>
<p>We detected: <span class="emotion-badge">{{ detected_emotion }}</span></p>
{% if music_links and music_links[0] != "No recommendation found for this emotion." %}
<h5 class="mt-4 mb-3">Recommended Songs For You:</h5>
<div class="song-list">
{% for link in music_links %}
<div class="song-item">
<a href="{{ link }}" class="song-link" target="_blank">
<i class="fas fa-play-circle"></i>
{{ link }}
</a>
</div>
{% endfor %}
</div>
{% else %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i> No music recommendations available for this emotion.
</div>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Webcam Section -->
<div class="col-lg-6 mb-4">
<div class="webcam-card h-100">
<div class="card-header">
<i class="fas fa-video me-2"></i> Live Webcam Detection
</div>
<div class="webcam-card-body">
<i class="fas fa-camera-retro webcam-icon"></i>
<p class="webcam-text">Capture your expression in real-time for emotion detection and music recommendations</p>
<a href="{{ url_for('webcam_page') }}" class="btn btn-primary webcam-button">
<i class="fas fa-camera-retro"></i> Open Webcam Capture
</a>
</div>
</div>
</div>
</div>
<!-- How It Works Section -->
<div class="row mt-5 pt-5" id="how-it-works">
<div class="col-12 mb-4">
<h2 class="section-title">How EmoSense Works</h2>
</div>
<div class="col-md-3 mb-4">
<div class="step-card">
<div class="step-number">1</div>
<div class="step-icon-circle">
<i class="fas fa-user step-icon"></i>
</div>
<h4 class="step-title">Upload Photo</h4>
<p>Upload your photo or use our webcam interface to capture your expression</p>
</div>
</div>
<div class="col-md-3 mb-4">
<div class="step-card">
<div class="step-number">2</div>
<div class="step-icon-circle">
<i class="fas fa-search step-icon"></i>
</div>
<h4 class="step-title">AI Analysis</h4>
<p>Our advanced AI analyzes your facial expressions and features</p>
</div>
</div>
<div class="col-md-3 mb-4">
<div class="step-card">
<div class="step-number">3</div>
<div class="step-icon-circle">
<i class="fas fa-smile step-icon"></i>
</div>
<h4 class="step-title">Emotion Detection</h4>
<p>Your emotion is detected accurately from your expression</p>
</div>
</div>
<div class="col-md-3 mb-4">
<div class="step-card">
<div class="step-number">4</div>
<div class="step-icon-circle">
<i class="fas fa-headphones step-icon"></i>
</div>
<h4 class="step-title">Music Match</h4>
<p>Get curated music recommendations tailored to your mood</p>
</div>
</div>
</div>
<!-- About Section -->
<div class="row mt-5 pt-5" id="about">
<div class="col-lg-8 mx-auto text-center">
<h2 class="section-title text-center mb-4">About EmoSense</h2>
<p class="lead">EmoSense combines advanced facial recognition technology with music psychology to create a unique emotion-based music recommendation system.</p>
<p>Our AI-powered platform analyzes facial expressions to detect emotions like happiness, sadness, anger, surprise, and more. We then match these emotions with music that complements or enhances your current mood, creating a personalized listening experience.</p>
<div class="mt-4">
<a href="#" class="btn btn-primary">
<i class="fas fa-info-circle me-2"></i> Learn More
</a>
</div>
</div>
</div>
</div>
<!-- Footer -->
<footer>
<div class="footer-blob"></div>
<div class="footer-blob2"></div>
<div class="container footer-content">
<div class="footer-links">
<a href="/">Home</a>
<a href="#how-it-works">How It Works</a>
<a href="#about">About</a>
<a href="#">Privacy Policy</a>
<a href="#">Terms of Service</a>
</div>
<div class="footer-social">
<a href="#" class="social-icon"><i class="fab fa-facebook-f"></i></a>
<a href="#" class="social-icon"><i class="fab fa-twitter"></i></a>
<a href="#" class="social-icon"><i class="fab fa-instagram"></i></a>
<a href="#" class="social-icon"><i class="fab fa-linkedin-in"></i></a>
</div>
<p>© 2025 EmoSense - AI-Powered Emotion-Based Music Recommendations</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Smooth scrolling for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (!target) return;
window.scrollTo({
top: target.offsetTop - 80,
behavior: 'smooth'
});
});
});
// Add animation to upload area on hover
const uploadArea = document.querySelector('.upload-area');
if (uploadArea) {
uploadArea.addEventListener('dragover', () => {
uploadArea.style.backgroundColor = 'rgba(108, 99, 255, 0.15)';
uploadArea.style.borderColor = 'var(--secondary)';
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.style.backgroundColor = 'rgba(108, 99, 255, 0.05)';
uploadArea.style.borderColor = 'var(--primary)';
});
}
</script>
</body>
</html>
now what i want is there should be admin and user panel like admin can see the db detatils like what are the user logged in deleted the user , and other functionalyion what admin have , and when user login it ill redifrect to the index.html page and there will also hace aonptopn to logout at in the header