![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/private_html/ |
<?php
// Define development mode (set to true for debugging, false for production)
define('DEVELOPMENT_MODE', false);
// Disable error reporting in production for security
error_reporting(0);
ini_set('display_errors', 0);
session_start();
// Add security headers
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');
// ALWAYS check if user is logged in - no exceptions
if (!isset($_SESSION['user_id'])) {
header('Location: /auth/login.php');
exit;
}
// Debug: Log that we're past the session check (only in development)
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Library.php: Session check passed, user_id: " . ($_SESSION['user_id'] ?? 'not set'));
}
// Handle track edit form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update_track') {
// Include database configuration
require_once 'config/database.php';
try {
$track_id = $_POST['track_id'];
$title = trim($_POST['title']);
$description = trim($_POST['description'] ?? '');
$price = floatval($_POST['price'] ?? 0);
$is_public = isset($_POST['is_public']) ? 1 : 0;
// Validate that the track belongs to the current user
$stmt = $pdo->prepare("SELECT user_id FROM music_tracks WHERE id = ?");
$stmt->execute([$track_id]);
$track = $stmt->fetch();
if (!$track || $track['user_id'] != $_SESSION['user_id']) {
$_SESSION['error_message'] = 'You can only edit your own tracks.';
header('Location: /library.php');
exit;
}
// Update the track
$stmt = $pdo->prepare("
UPDATE music_tracks
SET title = ?, description = ?, price = ?, is_public = ?, updated_at = NOW()
WHERE id = ? AND user_id = ?
");
$result = $stmt->execute([$title, $description, $price, $is_public, $track_id, $_SESSION['user_id']]);
if ($result) {
$_SESSION['success_message'] = 'Track updated successfully!';
} else {
$_SESSION['error_message'] = 'Failed to update track. Please try again.';
}
} catch (Exception $e) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Error updating track: " . $e->getMessage());
}
$_SESSION['error_message'] = 'An error occurred while updating the track.';
}
header('Location: /library.php');
exit;
}
// Helper functions for formatting
function formatBytes($bytes, $precision = 2) {
if ($bytes == 0) return '0 B';
$units = ['B', 'KB', 'MB', 'GB'];
$base = log($bytes, 1024);
$unit = $units[floor($base)];
return round(pow(1024, $base - floor($base)), $precision) . ' ' . $unit;
}
function formatTime($seconds) {
if ($seconds < 60) {
return round($seconds, 1) . 's';
} elseif ($seconds < 3600) {
$minutes = floor($seconds / 60);
$remainingSeconds = $seconds % 60;
return $minutes . 'm ' . round($remainingSeconds) . 's';
} else {
$hours = floor($seconds / 3600);
$minutes = floor(($seconds % 3600) / 60);
return $hours . 'h ' . $minutes . 'm';
}
}
// Include database configuration
require_once 'config/database.php';
// Debug: Test database connection (only in development)
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Library.php: Database config loaded");
}
// Get user info with error handling
try {
$user = getUserById($_SESSION['user_id']);
$user_name = $user['name'] ?? 'User';
} catch (Exception $e) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Error getting user info: " . $e->getMessage());
}
$user_name = 'User';
}
$credits = $_SESSION['credits'] ?? 5;
// Get user stats
$pdo = getDBConnection();
if (!$pdo) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Database connection failed in library.php");
}
die("Unable to connect to the database. Please try again later.");
}
$stmt = $pdo->prepare("
SELECT
COUNT(*) as total_tracks,
COUNT(CASE WHEN status = 'complete' THEN 1 END) as completed_tracks,
COUNT(CASE WHEN status = 'processing' THEN 1 END) as processing_tracks,
COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed_tracks,
AVG(CASE WHEN status = 'complete' THEN duration END) as avg_duration,
SUM(CASE WHEN status = 'complete' THEN duration END) as total_duration
FROM music_tracks
WHERE user_id = ?
");
try {
$stmt->execute([$_SESSION['user_id']]);
$user_stats = $stmt->fetch();
} catch (Exception $e) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Error getting user stats: " . $e->getMessage());
}
$user_stats = [
'total_tracks' => 0,
'completed_tracks' => 0,
'processing_tracks' => 0,
'failed_tracks' => 0,
'avg_duration' => 0,
'total_duration' => 0
];
}
// Get user's music tracks with enhanced data and variations
$status_filter = $_GET['status'] ?? 'all';
$sort_filter = $_GET['sort'] ?? 'latest';
// Build WHERE clause for status filtering
$where_clause = "WHERE mt.user_id = ?";
$params = [$_SESSION['user_id']];
if ($status_filter !== 'all') {
$where_clause .= " AND mt.status = ?";
$params[] = $status_filter;
}
// Build ORDER BY clause for sorting
$order_clause = "ORDER BY ";
switch ($sort_filter) {
case 'oldest':
$order_clause .= "mt.created_at ASC";
break;
case 'popular':
$order_clause = "LEFT JOIN (SELECT track_id, COUNT(*) as like_count FROM track_likes GROUP BY track_id) likes ON mt.id = likes.track_id ORDER BY likes.like_count DESC, mt.created_at DESC";
break;
case 'most-played':
$order_clause = "LEFT JOIN (SELECT track_id, COUNT(*) as play_count FROM track_plays GROUP BY track_id) plays ON mt.id = plays.track_id ORDER BY plays.play_count DESC, mt.created_at DESC";
break;
case 'latest':
default:
$order_clause .= "mt.created_at DESC";
break;
}
// Get tracks
try {
// Check if audio_variations table exists
$checkTable = $pdo->query("SHOW TABLES LIKE 'audio_variations'");
if ($checkTable->rowCount() > 0) {
$stmt = $pdo->prepare("
SELECT
mt.*,
COALESCE(vars.variation_count, 0) as variation_count,
CASE
WHEN mt.created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR) THEN 'π₯ Hot'
WHEN mt.created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR) THEN 'β New'
ELSE ''
END as badge
FROM music_tracks mt
LEFT JOIN (
SELECT track_id, COUNT(*) as variation_count
FROM audio_variations
GROUP BY track_id
) vars ON mt.id = vars.track_id
$where_clause
$order_clause
");
} else {
// Fallback query without audio_variations table
$stmt = $pdo->prepare("
SELECT
mt.*,
0 as variation_count,
CASE
WHEN mt.created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR) THEN 'π₯ Hot'
WHEN mt.created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR) THEN 'β New'
ELSE ''
END as badge
FROM music_tracks mt
$where_clause
$order_clause
");
}
} catch (Exception $e) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Error preparing tracks query: " . $e->getMessage());
}
// Fallback to simple query
$stmt = $pdo->prepare("
SELECT
mt.*,
0 as variation_count,
'' as badge
FROM music_tracks mt
$where_clause
ORDER BY mt.created_at DESC
");
}
try {
$stmt->execute($params);
$user_tracks = $stmt->fetchAll();
} catch (Exception $e) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Error getting user tracks: " . $e->getMessage());
}
$user_tracks = [];
}
// Get variations for each track
$tracks_with_variations = [];
foreach ($user_tracks as $track) {
$track['variations'] = [];
if ($track['variation_count'] > 0) {
try {
// Check if audio_variations table exists
$checkTable = $pdo->query("SHOW TABLES LIKE 'audio_variations'");
if ($checkTable->rowCount() > 0) {
$stmt = $pdo->prepare("
SELECT
variation_index,
audio_url,
duration,
title,
tags,
image_url,
source_audio_url,
metadata,
created_at
FROM audio_variations
WHERE track_id = ?
ORDER BY variation_index ASC
");
$stmt->execute([$track['id']]);
$track['variations'] = $stmt->fetchAll();
// Debug: Log variations found
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("π΅ Track {$track['id']}: Found " . count($track['variations']) . " variations");
}
} else {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("audio_variations table does not exist");
}
$track['variations'] = [];
}
} catch (Exception $e) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("Error getting variations for track {$track['id']}: " . $e->getMessage());
}
$track['variations'] = [];
}
} else {
// Debug: Log when no variations are expected
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("π΅ Track {$track['id']}: No variations expected (variation_count = {$track['variation_count']})");
}
}
$tracks_with_variations[] = $track;
}
// Set page variables for header
$current_page = 'library';
$page_title = 'My Library - SoundStudioPro';
$page_description = 'Your personal music library on SoundStudioPro.';
include 'includes/header.php';
// Build time filter condition
$time_condition = '';
switch ($time_filter) {
case 'today':
$time_condition = 'AND mt.created_at >= CURDATE()';
break;
case 'week':
$time_condition = 'AND mt.created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)';
break;
case 'month':
$time_condition = 'AND mt.created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)';
break;
case 'all':
default:
$time_condition = '';
break;
}
// Build genre filter condition
$genre_condition = '';
$genre_params = [];
if (!empty($genre_filter) && $genre_filter !== 'all') {
$genre_condition = "AND (JSON_UNQUOTE(JSON_EXTRACT(mt.metadata, '$.genre')) = ? OR JSON_CONTAINS(mt.metadata, JSON_QUOTE(?), '$.genres'))";
$genre_params = [$genre_filter, $genre_filter];
}
// Enhanced sort order function with proper handling
function getSortOrder($sort_filter) {
// Debug logging
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("π΅ Library: Sorting by '$sort_filter'");
}
switch ($sort_filter) {
case 'latest':
return "mt.created_at DESC";
case 'oldest':
return "mt.created_at ASC";
case 'popular':
return "(SELECT COUNT(*) FROM track_likes WHERE track_id = mt.id) DESC, mt.created_at DESC";
case 'most-played':
return "(SELECT COUNT(*) FROM track_plays WHERE track_id = mt.id) DESC, mt.created_at DESC";
case 'most-viewed':
return "(SELECT COUNT(*) FROM track_views WHERE track_id = mt.id) DESC, mt.created_at DESC";
case 'most-commented':
return "(SELECT COUNT(*) FROM track_comments WHERE track_id = mt.id) DESC, mt.created_at DESC";
case 'most-shared':
return "(SELECT COUNT(*) FROM track_shares WHERE track_id = mt.id) DESC, mt.created_at DESC";
case 'title':
return "mt.title ASC";
case 'artist':
return "u.name ASC, mt.created_at DESC";
case 'duration':
return "mt.duration DESC, mt.created_at DESC";
default:
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("π΅ Library: Unknown sort filter '$sort_filter', using default");
}
return "mt.created_at DESC";
}
}
// Load all tracks at once (no pagination)
$per_page = 1000; // Large number to get all tracks
$offset = 0;
// Get community tracks with social data and artist info (safe query)
try {
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.prompt,
mt.audio_url,
mt.duration,
mt.created_at,
mt.user_id,
mt.price,
mt.metadata,
u.name as artist_name,
u.profile_image,
u.plan,
COALESCE((SELECT COUNT(*) FROM track_likes WHERE track_id = mt.id), 0) as like_count,
COALESCE((SELECT COUNT(*) FROM track_comments WHERE track_id = mt.id), 0) as comment_count,
COALESCE((SELECT COUNT(*) FROM track_shares WHERE track_id = mt.id), 0) as share_count,
COALESCE((SELECT COUNT(*) FROM track_plays WHERE track_id = mt.id), 0) as play_count,
COALESCE((SELECT COUNT(*) FROM track_views WHERE track_id = mt.id), 0) as view_count,
COALESCE((SELECT COUNT(*) FROM user_follows WHERE follower_id = ? AND following_id = mt.user_id), 0) as is_following,
COALESCE((SELECT COUNT(*) FROM track_likes WHERE track_id = mt.id AND user_id = ?), 0) as user_liked,
COALESCE((SELECT COUNT(*) FROM music_tracks WHERE user_id = mt.user_id AND status = 'complete'), 0) as artist_total_tracks
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
$time_condition
$genre_condition
ORDER BY " . getSortOrder($sort_filter) . "
LIMIT $per_page OFFSET $offset
");
$params = [$user_id ?? 0, $user_id ?? 0];
if (!empty($genre_filter)) {
$params = array_merge($params, $genre_params);
}
$stmt->execute($params);
$recent_tracks = $stmt->fetchAll();
} catch (Exception $e) {
// Fallback to basic query if social features fail
$stmt = $pdo->prepare("
SELECT
mt.id,
mt.title,
mt.prompt,
mt.audio_url,
mt.duration,
mt.created_at,
mt.user_id,
mt.price,
mt.metadata,
u.name as artist_name,
u.profile_image,
u.plan,
0 as like_count,
0 as comment_count,
0 as share_count,
0 as play_count,
0 as view_count,
0 as is_following,
0 as user_liked,
1 as artist_total_tracks
FROM music_tracks mt
JOIN users u ON mt.user_id = u.id
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
$time_condition
$genre_condition
ORDER BY mt.created_at DESC
LIMIT $per_page OFFSET $offset
");
$fallback_params = [];
if (!empty($genre_filter)) {
$fallback_params = $genre_params;
}
$stmt->execute($fallback_params);
$recent_tracks = $stmt->fetchAll();
}
// Get all available genres from tracks
$genres_query = $pdo->prepare("
SELECT DISTINCT JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.genre')) as genre
FROM music_tracks
WHERE status = 'complete'
AND metadata IS NOT NULL
AND JSON_EXTRACT(metadata, '$.genre') IS NOT NULL
AND JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.genre')) != ''
ORDER BY genre ASC
");
$genres_query->execute();
$available_genres = $genres_query->fetchAll(PDO::FETCH_COLUMN);
// Add popular genres if missing from database
$popular_genres = ['Electronic', 'House', 'Techno', 'Pop', 'Hip Hop', 'Rock', 'Jazz', 'Classical', 'Ambient', 'Trance', 'Dubstep', 'R&B', 'Reggae', 'Country', 'Folk', 'Blues', 'Funk', 'Disco', 'Drum & Bass', 'Progressive', 'Chillout', 'Lofi'];
foreach ($popular_genres as $genre) {
if (!in_array($genre, $available_genres)) {
$available_genres[] = $genre;
}
}
$available_genres = array_unique($available_genres);
sort($available_genres);
// Get total count for pagination
$count_stmt = $pdo->prepare("
SELECT COUNT(*) as total
FROM music_tracks mt
WHERE mt.status = 'complete'
AND mt.audio_url IS NOT NULL
AND mt.audio_url != ''
$time_condition
");
$count_stmt->execute();
$total_tracks = $count_stmt->fetch()['total'];
$total_pages = ceil($total_tracks / $per_page);
// Get community stats
$community_stats = [
'total_tracks' => $total_tracks,
'total_artists' => count(array_unique(array_column($recent_tracks, 'artist_name'))),
'total_likes' => 0,
'total_comments' => 0,
'total_shares' => 0
];
?>
<div class="main-content">
<style>
body {
padding-bottom: 120px; /* Space for global player */
}
/* Hero Section Styling */
.hero {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
padding: 6rem 0 4rem;
position: relative;
overflow: hidden;
}
.hero .container {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 300px;
}
.hero-content {
flex: 1;
max-width: 600px;
}
.hero-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: rgba(102, 126, 234, 0.2);
color: #667eea;
padding: 0.8rem 1.5rem;
border-radius: 25px;
font-size: 1rem;
font-weight: 600;
margin-bottom: 2rem;
border: 1px solid rgba(102, 126, 234, 0.3);
}
.hero-title {
font-size: 3rem;
font-weight: 800;
margin-bottom: 1.5rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1.2;
}
.hero-subtitle {
font-size: 1.1rem;
color: #a0aec0;
line-height: 1.6;
margin-bottom: 2rem;
}
.hero-visual {
flex: 1;
max-width: 400px;
height: 300px;
position: relative;
}
/* Floating Notes Animation */
.floating-elements {
position: relative;
height: 100%;
width: 100%;
}
.floating-note {
position: absolute;
font-size: 2.5rem;
color: rgba(102, 126, 234, 0.4);
animation: float 8s ease-in-out infinite;
user-select: none;
pointer-events: none;
}
.floating-note:nth-child(1) {
top: 20%;
left: 10%;
animation-delay: 0s;
font-size: 3rem;
}
.floating-note:nth-child(2) {
top: 60%;
right: 15%;
animation-delay: 1.6s;
font-size: 2.5rem;
}
.floating-note:nth-child(3) {
bottom: 30%;
left: 20%;
animation-delay: 3.2s;
font-size: 2.8rem;
}
.floating-note:nth-child(4) {
top: 40%;
right: 30%;
animation-delay: 4.8s;
font-size: 2.2rem;
}
.floating-note:nth-child(5) {
bottom: 60%;
left: 50%;
animation-delay: 6.4s;
font-size: 2.6rem;
}
@keyframes float {
0%, 100% {
transform: translateY(0px) rotate(0deg);
opacity: 0.4;
}
25% {
transform: translateY(-15px) rotate(5deg);
opacity: 0.7;
}
50% {
transform: translateY(-25px) rotate(10deg);
opacity: 1;
}
75% {
transform: translateY(-15px) rotate(5deg);
opacity: 0.7;
}
}
/* Library-specific Styles */
.empty-state {
text-align: center;
padding: 4rem 2rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.3) 0%,
rgba(15, 15, 25, 0.4) 100%);
border-radius: 20px;
border: 1px solid rgba(102, 126, 234, 0.2);
margin: 2rem 0;
}
.empty-icon {
font-size: 4rem;
color: #667eea;
margin-bottom: 1rem;
opacity: 0.7;
}
.empty-title {
font-size: 1.5rem;
font-weight: 700;
color: #e2e8f0;
margin-bottom: 1rem;
}
.empty-description {
color: #a0aec0;
margin-bottom: 2rem;
line-height: 1.6;
}
.create-first-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 12px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.create-first-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.track-badge {
display: inline-block;
background: linear-gradient(135deg, #f59e0b, #d97706);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 1rem;
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
}
.status-indicator {
margin-top: 0.5rem;
}
.status-complete {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 2px 8px rgba(72, 187, 120, 0.3);
}
.status-processing {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
}
.status-failed {
background: linear-gradient(135deg, #ef4444, #dc2626);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
}
.error-message-modern {
background: linear-gradient(135deg,
rgba(239, 68, 68, 0.1) 0%,
rgba(220, 38, 38, 0.05) 100%);
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 12px;
padding: 1rem;
margin: 1rem 1.5rem;
}
.error-header {
color: #ef4444;
font-weight: 700;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.error-content {
color: #fca5a5;
font-size: 0.9rem;
line-height: 1.5;
margin-bottom: 0.5rem;
}
.error-tip {
background: rgba(102, 126, 234, 0.1);
border-left: 3px solid #667eea;
padding: 0.5rem 1rem;
border-radius: 0 8px 8px 0;
color: #a0aec0;
font-size: 0.85rem;
font-style: italic;
}
.track-actions-modern {
display: flex;
gap: 1.2rem;
padding: 1.5rem;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.action-btn {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.4);
color: #667eea;
padding: 1rem 1.8rem;
border-radius: 12px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
display: flex;
align-items: center;
gap: 0.6rem;
min-width: 120px;
justify-content: center;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.action-btn:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
border-color: rgba(102, 126, 234, 0.6);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.action-btn.primary {
background: linear-gradient(135deg, #667eea, #764ba2);
border-color: #667eea;
color: white;
}
.action-btn.primary:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.action-btn.primary.playing {
background: linear-gradient(135deg, #f59e0b, #d97706);
border-color: #f59e0b;
animation: playingPulse 2s ease-in-out infinite;
}
.action-btn.danger {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(220, 38, 38, 0.2));
border-color: rgba(239, 68, 68, 0.4);
color: #ef4444;
}
.action-btn.danger:hover {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.3), rgba(220, 38, 38, 0.3));
border-color: rgba(239, 68, 68, 0.6);
}
/* Enhanced button states */
.action-btn:active {
transform: translateY(1px);
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.2);
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.action-btn.loading {
position: relative;
color: transparent;
}
.action-btn.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 16px;
height: 16px;
margin: -8px 0 0 -8px;
border: 2px solid transparent;
border-top: 2px solid currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes playingPulse {
0%, 100% { box-shadow: 0 0 20px rgba(245, 158, 11, 0.5); }
50% { box-shadow: 0 0 30px rgba(245, 158, 11, 0.8); }
}
/* Button text improvements */
.action-btn i {
font-size: 0.9rem;
flex-shrink: 0;
}
.action-btn span {
font-size: inherit;
line-height: 1.2;
letter-spacing: 0.02em;
}
/* Ensure buttons don't shrink too much */
.action-btn {
flex-shrink: 0;
}
/* Music Metadata Styles */
.music-metadata {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
gap: 0.8rem;
margin-top: 1rem;
padding: 1rem;
background: rgba(102, 126, 234, 0.05);
border-radius: 12px;
border: 1px solid rgba(102, 126, 234, 0.1);
}
.music-metadata .metadata-item {
text-align: center;
padding: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.music-metadata .metadata-label {
font-size: 0.8rem;
color: #a0aec0;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0.3rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.3rem;
}
.music-metadata .metadata-label i {
font-size: 0.7rem;
color: #667eea;
}
.music-metadata .metadata-value {
font-size: 1rem;
color: #667eea;
font-weight: 700;
font-family: 'Courier New', monospace;
}
/* Genre Tags */
.genre-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
padding: 0.5rem 0;
}
.genre-tag {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.3);
color: #667eea;
padding: 0.4rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
cursor: default;
}
.genre-tag:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
border-color: rgba(102, 126, 234, 0.5);
transform: translateY(-1px);
}
.genre-tag.more {
background: linear-gradient(135deg, rgba(156, 163, 175, 0.2), rgba(107, 114, 128, 0.2));
border-color: rgba(156, 163, 175, 0.3);
color: #9ca3af;
font-style: italic;
}
.genre-tag.more:hover {
background: linear-gradient(135deg, rgba(156, 163, 175, 0.3), rgba(107, 114, 128, 0.3));
border-color: rgba(156, 163, 175, 0.5);
}
/* Variation Tags on Main Cards */
.variation-tags-main {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
padding: 0.5rem 0;
}
.variation-tag-main {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.2), rgba(217, 119, 6, 0.2));
border: 1px solid rgba(245, 158, 11, 0.3);
color: #f59e0b;
padding: 0.4rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
cursor: default;
}
.variation-tag-main:hover {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.3), rgba(217, 119, 6, 0.3));
border-color: rgba(245, 158, 11, 0.5);
transform: translateY(-1px);
}
.variation-tag-main.more {
background: linear-gradient(135deg, rgba(156, 163, 175, 0.2), rgba(107, 114, 128, 0.2));
border-color: rgba(156, 163, 175, 0.3);
color: #9ca3af;
font-style: italic;
}
.variation-tag-main.more:hover {
background: linear-gradient(135deg, rgba(156, 163, 175, 0.3), rgba(107, 114, 128, 0.3));
border-color: rgba(156, 163, 175, 0.5);
}
/* Variation Indicator */
.variation-indicator {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.3);
color: #667eea;
padding: 0.4rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
cursor: default;
display: flex;
align-items: center;
gap: 0.3rem;
}
.variation-indicator i {
font-size: 0.7rem;
}
/* DJ Tech Grid (like community_fixed.php) */
.dj-tech-compact {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.8rem;
padding: 1rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.2) 0%,
rgba(15, 15, 25, 0.3) 100%);
border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.tech-item {
text-align: center;
padding: 0.8rem;
background: rgba(255, 255, 255, 0.03);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
}
.tech-item:hover {
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.tech-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
color: #a0aec0;
margin-bottom: 0.3rem;
display: block;
}
.tech-value {
font-size: 1rem;
font-weight: 800;
color: #e2e8f0;
display: block;
}
/* Enhanced Track Genres */
.track-genres {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.02);
border-top: 1px solid rgba(255, 255, 255, 0.05);
position: relative;
}
.genre-tag {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.3);
color: #667eea;
padding: 0.4rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
cursor: default;
}
.genre-tag:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
border-color: rgba(102, 126, 234, 0.5);
transform: translateY(-1px);
}
.genre-tag.expand-btn {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.2), rgba(217, 119, 6, 0.2));
border-color: rgba(245, 158, 11, 0.3);
color: #f59e0b;
cursor: pointer;
}
.genre-tag.expand-btn:hover {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.3), rgba(217, 119, 6, 0.3));
border-color: rgba(245, 158, 11, 0.5);
transform: translateY(-1px);
}
.genre-tag.expand-btn.expanded {
background: linear-gradient(135deg, rgba(34, 197, 94, 0.2), rgba(22, 163, 74, 0.2));
border-color: rgba(34, 197, 94, 0.3);
color: #22c55e;
}
.genre-tag.expand-btn.expanded:hover {
background: linear-gradient(135deg, rgba(34, 197, 94, 0.3), rgba(22, 163, 74, 0.3));
border-color: rgba(34, 197, 94, 0.5);
}
.expanded-genres {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
/* Glow Effects for Playing Track */
.track-card.currently-playing {
border: 2px solid #f59e0b;
box-shadow:
0 0 30px rgba(245, 158, 11, 0.5),
0 20px 60px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
animation: playingGlow 2s ease-in-out infinite alternate;
}
@keyframes playingGlow {
0% { box-shadow: 0 0 30px rgba(245, 158, 11, 0.5), 0 20px 60px rgba(0, 0, 0, 0.4); }
100% { box-shadow: 0 0 50px rgba(245, 158, 11, 0.8), 0 20px 60px rgba(0, 0, 0, 0.4); }
}
/* Audio Visualization Effect */
.waveform-visualizer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 4px;
background: linear-gradient(90deg,
#667eea 0%,
#764ba2 25%,
#48bb78 50%,
#f59e0b 75%,
#ef4444 100%);
opacity: 0;
transition: opacity 0.3s ease;
animation: audioWave 2s ease-in-out infinite;
}
.track-card.currently-playing .waveform-visualizer {
opacity: 1;
}
@keyframes audioWave {
0%, 100% { transform: scaleX(0.3); }
50% { transform: scaleX(1); }
}
/* Generation Parameters Styles */
.generation-params {
background: linear-gradient(135deg,
rgba(15, 15, 35, 0.95) 0%,
rgba(25, 25, 45, 0.9) 100%);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 16px;
margin: 1rem 0;
overflow: hidden;
}
.params-header {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
color: #667eea;
text-transform: uppercase;
letter-spacing: 0.5px;
font-size: 0.9rem;
}
.params-header i {
font-size: 1rem;
}
.params-header .toggle-icon {
margin-left: auto;
transition: transform 0.3s ease;
}
.params-header:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.15), rgba(118, 75, 162, 0.15));
cursor: pointer;
}
.params-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.8rem;
padding: 1rem;
}
.param-item {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 0.8rem;
text-align: center;
transition: all 0.3s ease;
}
.param-item:hover {
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-1px);
}
.param-label {
display: block;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #a0aec0;
margin-bottom: 0.3rem;
}
.param-value {
display: block;
font-size: 0.9rem;
font-weight: 800;
color: #e2e8f0;
font-family: 'Courier New', monospace;
}
/* Compact Waveform (like community_fixed.php) */
.waveform-compact {
height: 40px;
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
padding: 0.5rem;
display: flex;
align-items: end;
gap: 2px;
cursor: pointer;
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.waveform-compact:hover {
background: rgba(102, 126, 234, 0.1);
}
.wave-bar {
flex: 1;
background: linear-gradient(to top, #667eea, #764ba2);
border-radius: 1px;
min-height: 8px;
transition: all 0.3s ease;
}
.waveform-compact:hover .wave-bar {
background: linear-gradient(to top, #5a67d8, #6b46c1);
}
.track-card.currently-playing .waveform-compact {
background: rgba(245, 158, 11, 0.1);
border: 1px solid rgba(245, 158, 11, 0.3);
}
.track-card.currently-playing .wave-bar {
background: linear-gradient(to top, #f59e0b, #d97706);
animation: waveformPulse 2s ease-in-out infinite;
}
@keyframes waveformPulse {
0%, 100% { opacity: 0.7; }
50% { opacity: 1; }
}
/* Unified Filter Styles */
.unified-filters {
background: linear-gradient(135deg, rgba(15, 15, 35, 0.95), rgba(25, 25, 45, 0.9));
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 16px;
padding: 1.5rem;
margin: 2rem 0;
backdrop-filter: blur(20px);
}
.filter-row {
display: flex;
gap: 1.5rem;
align-items: center;
flex-wrap: wrap;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 150px;
}
.filter-label {
font-size: 0.9rem;
color: #a0aec0;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.filter-select {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: white;
padding: 0.8rem 1rem;
font-size: 1rem;
transition: all 0.3s ease;
cursor: pointer;
}
.filter-select:hover {
border-color: rgba(102, 126, 234, 0.3);
background: rgba(255, 255, 255, 0.08);
}
.filter-select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
}
/* Infinite Scroll Styles */
.infinite-scroll-loader {
text-align: center;
padding: 3rem 0;
margin-top: 2rem;
}
.loading-spinner {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
color: #667eea;
font-size: 1.2rem;
font-weight: 600;
}
.loading-spinner i {
font-size: 1.5rem;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Modal Styles */
.variations-modal, .lyrics-modal, .download-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
display: none;
align-items: center;
justify-content: center;
z-index: 999999;
}
.variations-modal.active, .lyrics-modal.active, .download-modal.active {
display: flex;
}
.variations-content, .lyrics-content, .download-content {
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.9) 0%,
rgba(15, 15, 25, 0.95) 100%);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 20px;
max-width: 90%;
max-height: 90%;
overflow-y: auto;
position: relative;
backdrop-filter: blur(20px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.variations-header, .lyrics-header, .download-header {
background: linear-gradient(135deg, #667eea, #764ba2);
padding: 1.5rem;
border-radius: 20px 20px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.variations-title, .lyrics-title, .download-title {
color: white;
font-size: 1.5rem;
font-weight: 700;
margin: 0;
}
.close-variations, .close-lyrics, .close-download {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 0.5rem;
border-radius: 50%;
transition: all 0.3s ease;
}
.close-variations:hover, .close-lyrics:hover, .close-download:hover {
background: rgba(255, 255, 255, 0.2);
}
.variations-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
padding: 1.5rem;
}
.variation-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 12px;
padding: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.variation-card:hover {
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.4);
transform: translateY(-2px);
}
.variation-card.selected {
border-color: #667eea;
background: rgba(102, 126, 234, 0.2);
}
.variation-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.variation-title {
font-weight: 600;
color: #e2e8f0;
}
.variation-index {
background: #667eea;
color: white;
padding: 0.2rem 0.5rem;
border-radius: 8px;
font-size: 0.8rem;
font-weight: 600;
}
.variation-duration {
color: #a0aec0;
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.variation-tags {
display: flex;
gap: 0.3rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.variation-tag {
background: rgba(102, 126, 234, 0.2);
color: #667eea;
padding: 0.2rem 0.5rem;
border-radius: 6px;
font-size: 0.7rem;
}
.variation-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.variation-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
cursor: pointer;
font-size: 0.8rem;
transition: all 0.3s ease;
}
.variation-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.variation-btn.play {
background: linear-gradient(135deg, #667eea, #764ba2);
border-color: #667eea;
}
.variation-btn.download {
background: linear-gradient(135deg, #48bb78, #38a169);
border-color: #48bb78;
}
.variation-btn.select {
background: linear-gradient(135deg, #f59e0b, #d97706);
border-color: #f59e0b;
}
.variation-btn.select.selected {
background: linear-gradient(135deg, #48bb78, #38a169);
border-color: #48bb78;
}
.variations-footer, .lyrics-footer, .download-footer {
padding: 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.3);
border-radius: 0 0 20px 20px;
}
.variations-info {
color: #a0aec0;
font-size: 0.9rem;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.variations-actions, .lyrics-actions, .download-actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
}
.variations-btn, .lyrics-btn, .download-btn {
padding: 0.8rem 1.5rem;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
border: none;
}
.variations-btn.cancel, .lyrics-btn.cancel, .download-btn.cancel {
background: rgba(255, 255, 255, 0.1);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.variations-btn.save {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.variations-btn.save:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.lyrics-btn.copy, .download-btn.download-all {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
}
.lyrics-body, .download-body {
padding: 1.5rem;
max-height: 400px;
overflow-y: auto;
}
/* Filter Controls */
.filter-controls {
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.4) 0%,
rgba(15, 15, 25, 0.6) 100%);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 16px;
padding: 1.5rem;
margin: 2rem 0;
backdrop-filter: blur(10px);
}
.filter-row {
display: flex;
gap: 1rem;
align-items: center;
flex-wrap: wrap;
}
.filter-group {
display: flex;
align-items: center;
gap: 0.5rem;
}
.filter-label {
font-size: 0.9rem;
font-weight: 600;
color: #a0aec0;
text-transform: uppercase;
letter-spacing: 1px;
}
.filter-select {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 0.9rem;
transition: all 0.3s ease;
}
.filter-select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
/* Mobile Responsive Design */
@media (max-width: 768px) {
.hero {
padding: 4rem 0 2rem;
}
.hero .container {
flex-direction: column;
text-align: center;
min-height: auto;
}
.hero-content {
max-width: 100%;
margin-bottom: 2rem;
}
.hero-title {
font-size: 2.5rem;
}
.filter-row {
flex-direction: column;
align-items: stretch;
}
.track-actions-modern {
flex-direction: column;
}
.action-btn {
justify-content: center;
}
.track-actions-modern {
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
.action-btn {
padding: 1.2rem 1.5rem;
font-size: 1.1rem;
min-width: 140px;
width: 100%;
max-width: 200px;
}
.track-card {
margin-bottom: 1.5rem;
}
.track-metadata {
grid-template-columns: repeat(2, 1fr);
}
.music-metadata {
grid-template-columns: repeat(2, 1fr);
gap: 0.6rem;
padding: 0.8rem;
}
.music-metadata .metadata-item {
padding: 0.4rem;
}
.music-metadata .metadata-label {
font-size: 0.7rem;
}
.music-metadata .metadata-value {
font-size: 0.9rem;
}
.genre-tags {
gap: 0.4rem;
}
.genre-tag {
padding: 0.3rem 0.6rem;
font-size: 0.7rem;
}
.variation-tag-main {
padding: 0.3rem 0.6rem;
font-size: 0.7rem;
}
.unified-filters {
padding: 1rem;
margin: 1rem 0;
}
.filter-row {
flex-direction: column;
gap: 1rem;
}
.filter-group {
min-width: 100%;
}
.filter-select {
padding: 1rem;
font-size: 1.1rem;
}
.dj-tech-compact {
grid-template-columns: 1fr;
gap: 0.5rem;
padding: 0.8rem;
}
.tech-item {
padding: 0.6rem;
}
.tech-label {
font-size: 0.6rem;
}
.tech-value {
font-size: 0.9rem;
}
.track-genres {
padding: 0.8rem;
}
.genre-tag {
padding: 0.3rem 0.6rem;
font-size: 0.7rem;
}
.generation-params {
margin: 0.8rem 0;
}
.params-grid {
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 0.6rem;
padding: 0.8rem;
}
.param-item {
padding: 0.6rem;
}
.param-label {
font-size: 0.6rem;
}
.param-value {
font-size: 0.8rem;
}
.waveform-compact {
height: 35px;
padding: 0.4rem;
margin-bottom: 0.8rem;
}
.wave-bar {
min-height: 6px;
}
}
.hero-visual {
max-width: 300px;
height: 200px;
}
.floating-note {
font-size: 2rem;
}
.floating-note:nth-child(1) { font-size: 2.2rem; }
.floating-note:nth-child(2) { font-size: 2rem; }
.floating-note:nth-child(3) { font-size: 2.1rem; }
.floating-note:nth-child(4) { font-size: 1.8rem; }
.floating-note:nth-child(5) { font-size: 2rem; }
.community-stats {
grid-template-columns: repeat(2, 1fr);
}
.tracks-grid {
grid-template-columns: 1fr;
}
}
.community-content {
padding: 4rem 0;
display: flex;
flex-direction: column;
align-items: center;
}
.community-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 2rem;
margin-bottom: 4rem;
}
.stat-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 2rem;
text-align: center;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.stat-number {
font-size: 2.5rem;
font-weight: 700;
color: #667eea;
margin-bottom: 0.5rem;
}
.stat-label {
color: #a0aec0;
font-size: 1.1rem;
}
.tracks-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 2rem;
}
.track-card {
background: linear-gradient(145deg,
rgba(15, 15, 35, 0.95) 0%,
rgba(25, 25, 45, 0.9) 50%,
rgba(35, 35, 55, 0.85) 100%);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 24px;
padding: 0;
margin-bottom: 2rem;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
overflow: hidden;
position: relative;
backdrop-filter: blur(20px);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 2px 16px rgba(102, 126, 234, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.track-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg,
transparent 0%,
#667eea 25%,
#764ba2 50%,
#667eea 75%,
transparent 100%);
opacity: 0;
transition: opacity 0.3s ease;
}
.track-card:hover {
transform: translateY(-8px) scale(1.02);
border-color: rgba(102, 126, 234, 0.5);
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.4),
0 8px 40px rgba(102, 126, 234, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
.track-card:hover::before {
opacity: 1;
}
/* Premium Artist Info Section */
.artist-info {
padding: 1.5rem 1.5rem 1rem;
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.1) 0%,
rgba(118, 75, 162, 0.05) 100%);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
position: relative;
}
.artist-profile img, .default-avatar {
width: 56px;
height: 56px;
border-radius: 16px;
border: 2px solid rgba(102, 126, 234, 0.3);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.track-card:hover .artist-profile img,
.track-card:hover .default-avatar {
border-color: rgba(102, 126, 234, 0.6);
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
transform: scale(1.05);
}
.default-avatar {
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 1.4rem;
color: white;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.track-artist {
font-size: 1rem;
color: #e2e8f0;
font-weight: 600;
margin-bottom: 0.3rem;
}
.artist-plan {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 2px 8px rgba(72, 187, 120, 0.3);
}
.artist-plan.pro {
background: linear-gradient(135deg, #f59e0b, #d97706);
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
}
.artist-plan.starter {
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);
}
.artist-stats {
display: flex;
gap: 1rem;
margin-top: 0.5rem;
font-size: 0.85rem;
color: #a0aec0;
}
.artist-stats span {
display: flex;
align-items: center;
gap: 0.3rem;
}
.artist-stats i {
color: #667eea;
font-size: 0.8rem;
}
/* Enhanced Follow Button */
.btn-follow {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.4);
color: #667eea;
padding: 0.6rem 1.2rem;
border-radius: 12px;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.btn-follow:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
border-color: rgba(102, 126, 234, 0.6);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.btn-follow.following {
background: linear-gradient(135deg, #48bb78, #38a169);
border-color: #48bb78;
color: white;
}
/* Premium Track Header */
.track-header {
padding: 0 1.5rem;
margin-bottom: 1rem;
}
.track-title {
font-size: 1.6rem;
font-weight: 800;
margin-bottom: 1rem;
background: linear-gradient(135deg, #e2e8f0, #f7fafc);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1.3;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Professional Track Prompt */
.track-prompt {
padding: 1rem 1.5rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.3) 0%,
rgba(15, 15, 25, 0.4) 100%);
border-left: 3px solid #667eea;
margin: 1rem 1.5rem;
border-radius: 0 8px 8px 0;
font-size: 0.95rem;
line-height: 1.5;
color: #cbd5e0;
font-style: italic;
position: relative;
}
.track-prompt::before {
content: '"';
position: absolute;
left: -0.5rem;
top: -0.5rem;
font-size: 2rem;
color: #667eea;
font-weight: bold;
}
/* Studio-Quality Waveform */
.track-waveform {
padding: 1.5rem;
margin: 1rem 1.5rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.6) 0%,
rgba(15, 15, 25, 0.8) 100%);
border-radius: 16px;
border: 1px solid rgba(102, 126, 234, 0.2);
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.track-waveform::before {
content: 'βΆ Click to Play';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(102, 126, 234, 0.9);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
opacity: 0;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.track-waveform:hover::before {
opacity: 1;
}
.track-waveform:hover {
border-color: rgba(102, 126, 234, 0.5);
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.waveform-bars {
display: flex;
align-items: end;
gap: 2px;
height: 60px;
justify-content: space-between;
}
.waveform-bar {
background: linear-gradient(to top,
#667eea 0%,
#764ba2 50%,
#48bb78 100%);
width: 3px;
border-radius: 2px;
transition: all 0.3s ease;
opacity: 0.7;
}
.waveform-bar:hover {
opacity: 1;
box-shadow: 0 0 8px rgba(102, 126, 234, 0.5);
}
/* Professional Metadata Grid */
.track-metadata {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 1rem;
padding: 1.5rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.2) 0%,
rgba(15, 15, 25, 0.3) 100%);
border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.metadata-item {
text-align: center;
padding: 0.8rem;
background: rgba(255, 255, 255, 0.03);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
}
.metadata-item:hover {
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.metadata-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
color: #a0aec0;
margin-bottom: 0.3rem;
}
.metadata-value {
font-size: 1rem;
font-weight: 800;
color: #e2e8f0;
}
/* Premium Track Price Section */
.track-price {
padding: 1.5rem;
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.1) 0%,
rgba(118, 75, 162, 0.05) 100%);
border-top: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.price-display {
font-size: 1.4rem;
font-weight: 800;
color: #48bb78;
display: flex;
align-items: center;
gap: 0.5rem;
}
.price-display.free {
color: #f59e0b;
}
.track-actions {
display: flex;
gap: 0.8rem;
align-items: center;
flex-wrap: wrap;
}
.play-track-btn, .add-to-cart-btn, .share-track-btn {
padding: 0.8rem 1.5rem;
border-radius: 12px;
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
text-decoration: none;
}
.play-track-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);
}
.play-track-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.5);
}
.play-track-btn.playing {
background: linear-gradient(135deg, #f59e0b, #d97706);
animation: playingPulse 2s infinite;
}
@keyframes playingPulse {
0%, 100% { box-shadow: 0 4px 16px rgba(245, 158, 11, 0.3); }
50% { box-shadow: 0 8px 32px rgba(245, 158, 11, 0.6); }
}
/* Initially disable play buttons until global player is ready */
.action-btn.play-btn, .play-track-btn {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
.action-btn.play-btn.ready, .play-track-btn.ready {
opacity: 1;
cursor: pointer;
pointer-events: auto;
}
.add-to-cart-btn {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
box-shadow: 0 4px 16px rgba(72, 187, 120, 0.3);
}
.add-to-cart-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(72, 187, 120, 0.5);
}
.share-track-btn {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
}
.share-track-btn:hover {
background: rgba(102, 126, 234, 0.2);
color: #667eea;
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
/* Social Interaction Buttons */
.social-actions {
padding: 2rem 1.5rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.4) 0%,
rgba(15, 15, 25, 0.6) 100%);
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.social-btn {
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0.05) 100%);
border: 2px solid rgba(255, 255, 255, 0.2);
color: #cbd5e0;
padding: 1.2rem 1rem;
border-radius: 20px;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
font-size: 0.9rem;
font-weight: 600;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.8rem;
position: relative;
overflow: hidden;
backdrop-filter: blur(10px);
text-align: center;
}
.social-btn::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, transparent, rgba(255, 255, 255, 0.1), transparent);
opacity: 0;
transition: opacity 0.3s ease;
}
.social-btn:hover::before {
opacity: 1;
}
.social-btn i {
font-size: 1.8rem;
transition: all 0.3s ease;
}
.social-count {
font-size: 1.1rem;
font-weight: 800;
letter-spacing: 0.5px;
}
/* Individual Social Button Styles */
.like-btn:hover {
background: linear-gradient(135deg, #ff6b9d, #e91e63);
border-color: #ff6b9d;
color: white;
transform: translateY(-8px) scale(1.05);
box-shadow: 0 15px 40px rgba(255, 107, 157, 0.4);
}
.like-btn.liked {
background: linear-gradient(135deg, #ff6b9d, #e91e63);
border-color: #ff6b9d;
color: white;
animation: heartBeat 1.5s ease-in-out infinite;
}
@keyframes heartBeat {
0%, 100% { transform: scale(1); }
25% { transform: scale(1.1); }
50% { transform: scale(1); }
75% { transform: scale(1.05); }
}
.comment-btn:hover {
background: linear-gradient(135deg, #4ecdc4, #20b2aa);
border-color: #4ecdc4;
color: white;
transform: translateY(-8px) scale(1.05);
box-shadow: 0 15px 40px rgba(78, 205, 196, 0.4);
}
.share-btn:hover {
background: linear-gradient(135deg, #667eea, #764ba2);
border-color: #667eea;
color: white;
transform: translateY(-8px) scale(1.05);
box-shadow: 0 15px 40px rgba(102, 126, 234, 0.4);
}
.stats-btn:hover {
background: linear-gradient(135deg, #feca57, #ff9ff3);
border-color: #feca57;
color: white;
transform: translateY(-8px) scale(1.05);
box-shadow: 0 15px 40px rgba(254, 202, 87, 0.4);
}
/* Enhanced DJ Tech Grid with Mood & Energy */
.dj-tech-grid {
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.6) 0%,
rgba(15, 15, 25, 0.8) 100%);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 20px;
padding: 2rem 1.5rem;
margin: 1.5rem;
cursor: pointer;
transition: all 0.4s ease;
position: relative;
overflow: hidden;
}
.dj-tech-grid::before {
content: 'ποΈ DJ MODE - Click to activate mixing';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(102, 126, 234, 0.95);
color: white;
padding: 0.8rem 1.5rem;
border-radius: 25px;
font-size: 0.9rem;
font-weight: 700;
opacity: 0;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
white-space: nowrap;
}
.dj-tech-grid:hover::before {
opacity: 1;
}
.dj-tech-grid:hover {
border-color: rgba(102, 126, 234, 0.8);
box-shadow:
0 20px 60px rgba(102, 126, 234, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
transform: translateY(-5px);
}
.tech-primary {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.tech-secondary {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
align-items: center;
}
.bpm-display, .key-display, .time-display, .mood-display, .energy-display {
text-align: center;
padding: 1rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
position: relative;
}
.bpm-display:hover, .key-display:hover, .time-display:hover,
.mood-display:hover, .energy-display:hover {
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-3px) scale(1.05);
}
.tech-label {
font-size: 0.8rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1.5px;
color: #a0aec0;
margin-bottom: 0.5rem;
display: block;
}
.tech-value {
font-size: 1.4rem;
font-weight: 900;
color: #e2e8f0;
display: block;
}
.bpm-value {
color: #ff6b6b;
text-shadow: 0 0 20px rgba(255, 107, 107, 0.5);
}
.key-value {
color: #4ecdc4;
text-shadow: 0 0 20px rgba(78, 205, 196, 0.5);
}
.mood-value {
color: #feca57;
text-shadow: 0 0 20px rgba(254, 202, 87, 0.5);
text-transform: capitalize;
}
.energy-value {
color: #ff9ff3;
text-shadow: 0 0 20px rgba(255, 159, 243, 0.5);
text-transform: capitalize;
}
/* Responsive for mobile */
@media (max-width: 768px) {
.social-actions {
grid-template-columns: repeat(2, 1fr);
gap: 0.8rem;
padding: 1.5rem 1rem;
}
.tech-primary {
grid-template-columns: repeat(2, 1fr);
}
.tech-secondary {
grid-template-columns: 1fr;
}
.social-btn {
padding: 1rem 0.8rem;
}
.social-btn i {
font-size: 1.5rem;
}
}
/* Mobile Responsive Design */
@media (max-width: 768px) {
.track-card {
border-radius: 16px;
margin-bottom: 1.5rem;
}
.track-title {
font-size: 1.3rem;
}
.track-metadata {
grid-template-columns: repeat(2, 1fr);
}
.track-price {
flex-direction: column;
align-items: stretch;
}
.track-actions {
justify-content: center;
}
}
.track-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.5rem;
}
.artist-profile img {
width: 50px;
height: 50px;
border-radius: 50%;
object-fit: cover;
}
.default-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1.2rem;
}
.track-title {
font-size: 1.4rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.track-artist {
color: #a0aec0;
}
.track-prompt {
color: #cbd5e0;
margin: 1rem 0;
line-height: 1.5;
}
.track-details {
display: flex;
gap: 1rem;
margin: 1rem 0;
color: #a0aec0;
font-size: 0.9rem;
}
.track-actions {
display: flex;
gap: 1rem;
margin: 1.5rem 0;
}
.play-track-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
}
.play-track-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.play-track-btn.playing {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
box-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
animation: playingPulse 2s ease-in-out infinite;
}
.play-track-btn.playing i {
animation: playingIcon 1s ease-in-out infinite alternate;
}
@keyframes playingPulse {
0%, 100% { box-shadow: 0 0 20px rgba(102, 126, 234, 0.5); }
50% { box-shadow: 0 0 30px rgba(102, 126, 234, 0.8); }
}
@keyframes playingIcon {
0% { transform: scale(1); }
100% { transform: scale(1.1); }
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 0.8rem 1.5rem;
border-radius: 8px;
text-decoration: none;
font-size: 0.9rem;
transition: all 0.3s ease;
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.social-actions {
display: flex;
gap: 1rem;
padding-top: 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.social-btn {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #a0aec0;
padding: 0.8rem 1.2rem;
border-radius: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.6rem;
font-size: 1rem;
font-weight: 500;
transition: all 0.3s ease;
}
.social-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: white;
}
.social-btn.liked {
color: #e53e3e;
background: rgba(229, 62, 62, 0.1);
}
.social-btn.following {
color: #48bb78;
background: rgba(72, 187, 120, 0.1);
}
/* Professional Music Feed Styles */
.artist-info {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.artist-details {
flex: 1;
}
.artist-plan {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
}
.artist-plan.pro {
background: linear-gradient(135deg, #f093fb, #f5576c);
}
.artist-plan.starter {
background: linear-gradient(135deg, #4facfe, #00f2fe);
}
.artist-stats {
display: flex;
gap: 1rem;
margin-top: 0.5rem;
font-size: 0.9rem;
color: #a0aec0;
}
.track-genre {
display: inline-block;
background: rgba(255, 255, 255, 0.1);
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
color: #cbd5e0;
margin-right: 0.5rem;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
}
.track-genre:hover {
background: rgba(102, 126, 234, 0.3);
color: white;
transform: translateY(-1px);
}
.track-genre.clickable {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.3);
}
.track-genre.clickable:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.4), rgba(118, 75, 162, 0.4));
border-color: rgba(102, 126, 234, 0.6);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.track-price {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 1rem;
}
.price-tag {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: 600;
}
.price-tag.free {
background: linear-gradient(135deg, #4299e1, #3182ce);
}
.btn-cart {
background: linear-gradient(135deg, #ed8936, #dd6b20);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 12px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn-cart:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(237, 137, 54, 0.4);
}
.btn-cart.added {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
}
.btn-cart:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.btn-follow {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
padding: 0.8rem 1.5rem;
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn-follow:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.btn-follow.following {
background: rgba(72, 187, 120, 0.2);
border-color: rgba(72, 187, 120, 0.5);
color: #48bb78;
}
.track-waveform {
height: 60px;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
margin: 1rem 0;
position: relative;
overflow: hidden;
cursor: pointer;
}
.waveform-bars {
display: flex;
align-items: end;
height: 100%;
padding: 8px;
gap: 2px;
}
.waveform-bar {
flex: 1;
background: rgba(102, 126, 234, 0.3);
border-radius: 2px;
transition: all 0.3s ease;
}
.waveform-bar.playing {
background: linear-gradient(180deg, #667eea, #764ba2);
}
.track-metadata {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 1rem;
margin: 1rem 0;
padding: 1rem;
background: rgba(255, 255, 255, 0.03);
border-radius: 8px;
font-size: 0.9rem;
}
.metadata-item {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.metadata-label {
color: #a0aec0;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.metadata-value {
color: white;
font-weight: 500;
}
/* Comments Modal */
.comments-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
}
.comments-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.comments-container {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 20px;
width: 100%;
max-width: 600px;
max-height: 80vh;
display: flex;
flex-direction: column;
}
.comments-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 2rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.comments-header h3 {
color: white;
font-size: 2rem;
margin: 0;
}
.close-btn {
background: none;
border: none;
color: white;
font-size: 2.4rem;
cursor: pointer;
padding: 0.5rem;
border-radius: 50%;
transition: all 0.3s ease;
}
.close-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
.comments-list {
flex: 1;
overflow-y: auto;
padding: 2rem;
max-height: 400px;
}
.comment-item {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
}
.comment-avatar {
flex-shrink: 0;
}
.default-avatar-small {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.6rem;
font-weight: 700;
}
.comment-avatar img {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.comment-content {
flex: 1;
}
.comment-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.5rem;
}
.comment-author {
color: white;
font-weight: 600;
}
.comment-time {
color: #a0aec0;
font-size: 0.9rem;
}
.comment-text {
color: #cbd5e0;
line-height: 1.5;
}
.comment-form {
padding: 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.comment-form textarea {
width: 100%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 1rem;
color: white;
resize: vertical;
min-height: 80px;
margin-bottom: 1rem;
}
.comment-form textarea::placeholder {
color: #a0aec0;
}
.comment-form .btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 12px;
cursor: pointer;
font-weight: 600;
}
.comment-form .btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.no-comments {
text-align: center;
color: #a0aec0;
padding: 2rem;
}
.loading {
text-align: center;
color: #a0aec0;
padding: 2rem;
}
/* Notification System */
.notification {
position: fixed;
top: 2rem;
right: 2rem;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 1rem 1.5rem;
color: white;
z-index: 10000;
opacity: 0;
transform: translateX(100%);
transition: all 0.3s ease;
}
.notification.show {
opacity: 1;
transform: translateX(0);
}
.notification-success {
border-left: 4px solid #48bb78;
}
.notification-error {
border-left: 4px solid #f56565;
}
.notification-warning {
border-left: 4px solid #ed8936;
}
/* Social Share Modal */
.share-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 1000;
backdrop-filter: blur(5px);
display: none;
}
.share-modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 20px;
padding: 2rem;
width: 90%;
max-width: 500px;
color: white;
}
.share-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.share-platforms {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.share-platform {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 15px;
padding: 1.5rem 1rem;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
color: white;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.share-platform:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}
.share-platform.twitter { background: rgba(29, 161, 242, 0.2); }
.share-platform.facebook { background: rgba(24, 119, 242, 0.2); }
.share-platform.instagram { background: rgba(225, 48, 108, 0.2); }
.share-platform.whatsapp { background: rgba(37, 211, 102, 0.2); }
.share-platform.linkedin { background: rgba(10, 102, 194, 0.2); }
.share-platform.discord { background: rgba(114, 137, 218, 0.2); }
.share-platform i {
font-size: 2rem;
}
.share-url-section {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 1rem;
}
.share-url-input {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
}
.share-url {
flex: 1;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 0.8rem;
color: white;
font-family: monospace;
font-size: 0.9rem;
}
.copy-btn {
background: linear-gradient(135deg, #48bb78, #38a169);
border: none;
color: white;
padding: 0.8rem 1.5rem;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
}
.copy-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(72, 187, 120, 0.4);
}
.copy-btn.copied {
background: linear-gradient(135deg, #667eea, #764ba2);
}
/* Track Highlight Effect */
.track-card.highlighted {
animation: trackHighlight 3s ease-in-out;
border: 2px solid rgba(102, 126, 234, 0.8);
box-shadow: 0 0 30px rgba(102, 126, 234, 0.4);
}
@keyframes trackHighlight {
0% {
box-shadow: 0 0 5px rgba(102, 126, 234, 0.4);
border-color: rgba(102, 126, 234, 0.3);
}
50% {
box-shadow: 0 0 40px rgba(102, 126, 234, 0.8);
border-color: rgba(102, 126, 234, 1);
}
100% {
box-shadow: 0 0 30px rgba(102, 126, 234, 0.4);
border-color: rgba(102, 126, 234, 0.8);
}
}
/* DJ Technical Information Grid */
.dj-tech-grid {
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.6) 0%,
rgba(15, 15, 25, 0.8) 100%);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 20px;
padding: 2rem 1.5rem;
margin: 1.5rem;
cursor: pointer;
transition: all 0.4s ease;
position: relative;
overflow: hidden;
}
.dj-tech-grid::before {
content: 'ποΈ DJ MODE - Click to activate mixing';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(102, 126, 234, 0.95);
color: white;
padding: 0.8rem 1.5rem;
border-radius: 25px;
font-size: 0.9rem;
font-weight: 700;
opacity: 0;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
white-space: nowrap;
}
.dj-tech-grid:hover::before {
opacity: 1;
}
.dj-tech-grid:hover {
border-color: rgba(102, 126, 234, 0.8);
box-shadow:
0 20px 60px rgba(102, 126, 234, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
transform: translateY(-5px);
}
.tech-primary {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.tech-secondary {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
align-items: center;
}
.bpm-display, .key-display, .time-display, .mood-display, .energy-display {
text-align: center;
padding: 1rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
position: relative;
}
.bpm-display:hover, .key-display:hover, .time-display:hover,
.mood-display:hover, .energy-display:hover {
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-3px) scale(1.05);
}
.tech-label {
font-size: 0.8rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1.5px;
color: #a0aec0;
margin-bottom: 0.5rem;
display: block;
}
.tech-value {
font-size: 1.4rem;
font-weight: 900;
color: #e2e8f0;
display: block;
}
.bpm-value {
color: #ff6b6b;
text-shadow: 0 0 20px rgba(255, 107, 107, 0.5);
}
.key-value {
color: #4ecdc4;
text-shadow: 0 0 20px rgba(78, 205, 196, 0.5);
}
.mood-value {
color: #feca57;
text-shadow: 0 0 20px rgba(254, 202, 87, 0.5);
text-transform: capitalize;
}
.energy-value {
color: #ff9ff3;
text-shadow: 0 0 20px rgba(255, 159, 243, 0.5);
text-transform: capitalize;
}
/* Responsive for mobile */
@media (max-width: 768px) {
.social-actions {
grid-template-columns: repeat(2, 1fr);
gap: 0.8rem;
padding: 1.5rem 1rem;
}
.tech-primary {
grid-template-columns: repeat(2, 1fr);
}
.tech-secondary {
grid-template-columns: 1fr;
}
.social-btn {
padding: 1rem 0.8rem;
}
.social-btn i {
font-size: 1.5rem;
}
}
/* Mobile responsive */
@media (max-width: 768px) {
.tech-primary {
grid-template-columns: repeat(2, 1fr);
}
.time-display {
grid-column: span 2;
}
.tech-secondary {
flex-direction: column;
align-items: flex-start;
}
.tech-value {
font-size: 1rem;
}
.bpm-value {
font-size: 1.1rem;
}
}
/* DJ Mixing Panel */
.dj-mixing-panel {
position: fixed;
top: 20px;
right: 20px;
width: 300px;
background: rgba(0, 0, 0, 0.95);
border: 2px solid #667eea;
border-radius: 15px;
backdrop-filter: blur(20px);
z-index: 10000;
display: none;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.dj-panel-header {
background: linear-gradient(135deg, #667eea, #764ba2);
padding: 1rem;
border-radius: 13px 13px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.dj-panel-header h3 {
margin: 0;
color: white;
font-size: 1.1rem;
}
.close-dj-panel {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.close-dj-panel:hover {
background: rgba(255, 255, 255, 0.2);
}
.dj-panel-content {
padding: 1rem;
color: white;
}
.current-track-info {
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(102, 126, 234, 0.3);
}
.dj-info-item {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.dj-info-item label {
color: #a0aec0;
font-weight: 600;
}
.dj-bpm {
color: #ff6b6b;
font-weight: 800;
font-size: 1.1rem;
}
.dj-key {
color: #48bb78;
font-weight: 800;
font-size: 1.1rem;
}
.mixing-suggestions h4 {
color: #667eea;
margin-bottom: 0.8rem;
font-size: 1rem;
}
.bpm-suggestions, .key-suggestions {
margin-bottom: 1rem;
}
.bpm-suggestions p, .key-suggestions p {
margin: 0.3rem 0;
font-size: 0.9rem;
color: #cbd5e0;
}
/* DJ Mode Highlighting */
.track-card.dj-selected {
border: 2px solid #667eea;
box-shadow: 0 0 25px rgba(102, 126, 234, 0.6);
transform: scale(1.02);
}
.track-card.perfect-mix-match {
border: 2px solid #48bb78;
box-shadow: 0 0 25px rgba(72, 187, 120, 0.6);
}
.track-card.perfect-mix-match::before {
content: "π― PERFECT MIX";
position: absolute;
top: 10px;
right: 10px;
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 15px;
font-size: 0.7rem;
font-weight: 700;
z-index: 5;
}
.bpm-compatible {
box-shadow: 0 0 15px rgba(255, 107, 107, 0.5) !important;
border-color: rgba(255, 107, 107, 0.6) !important;
}
.key-compatible {
box-shadow: 0 0 15px rgba(72, 187, 120, 0.5) !important;
border-color: rgba(72, 187, 120, 0.6) !important;
}
/* DJ Mode Cursor */
.dj-tech-grid {
cursor: pointer;
transition: all 0.3s ease;
}
.dj-tech-grid:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
/* Premium Animated Effects */
.track-card::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent,
rgba(102, 126, 234, 0.1),
transparent);
transition: left 0.5s ease;
pointer-events: none;
}
.track-card:hover::after {
left: 100%;
}
/* Animated Music Particles */
.track-card .music-particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
}
.track-card:hover .music-particles {
opacity: 1;
}
.music-particle {
position: absolute;
width: 4px;
height: 4px;
background: #667eea;
border-radius: 50%;
animation: floatParticle 4s linear infinite;
}
.music-particle:nth-child(2) { left: 20%; animation-delay: 0.5s; background: #764ba2; }
.music-particle:nth-child(3) { left: 40%; animation-delay: 1s; background: #48bb78; }
.music-particle:nth-child(4) { left: 60%; animation-delay: 1.5s; background: #f59e0b; }
.music-particle:nth-child(5) { left: 80%; animation-delay: 2s; background: #ef4444; }
@keyframes floatParticle {
0% {
bottom: 0;
opacity: 1;
transform: translateX(0);
}
100% {
bottom: 100%;
opacity: 0;
transform: translateX(20px);
}
}
/* Glow Effects for Playing Track */
.track-card.currently-playing {
border: 2px solid #f59e0b;
box-shadow:
0 0 30px rgba(245, 158, 11, 0.5),
0 20px 60px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
animation: playingGlow 2s ease-in-out infinite alternate;
}
@keyframes playingGlow {
0% { box-shadow: 0 0 30px rgba(245, 158, 11, 0.5), 0 20px 60px rgba(0, 0, 0, 0.4); }
100% { box-shadow: 0 0 50px rgba(245, 158, 11, 0.8), 0 20px 60px rgba(0, 0, 0, 0.4); }
}
/* Enhanced Button Animations */
.play-track-btn {
position: relative;
overflow: hidden;
}
.play-track-btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: all 0.6s ease;
}
.play-track-btn:active::before {
width: 300px;
height: 300px;
}
/* Premium Loading States */
.track-card.loading {
position: relative;
overflow: hidden;
}
.track-card.loading::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent,
rgba(102, 126, 234, 0.3),
transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
/* Audio Visualization Effect */
.waveform-visualizer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 4px;
background: linear-gradient(90deg,
#667eea 0%,
#764ba2 25%,
#48bb78 50%,
#f59e0b 75%,
#ef4444 100%);
opacity: 0;
transition: opacity 0.3s ease;
animation: audioWave 2s ease-in-out infinite;
}
.track-card.currently-playing .waveform-visualizer {
opacity: 1;
}
@keyframes audioWave {
0%, 100% { transform: scaleX(0.3); }
50% { transform: scaleX(1); }
}
/* Premium Tooltips */
.tooltip {
position: relative;
}
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: 120%;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 0.8rem;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
z-index: 1000;
}
.tooltip:hover::after {
opacity: 1;
visibility: visible;
}
/* Enhanced Pricing Display */
.price-section {
margin-bottom: 1.5rem;
}
.price-display {
background: linear-gradient(135deg,
rgba(72, 187, 120, 0.2) 0%,
rgba(72, 187, 120, 0.1) 100%);
border: 2px solid rgba(72, 187, 120, 0.4);
border-radius: 16px;
padding: 1rem 1.5rem;
text-align: center;
transition: all 0.3s ease;
}
.price-display.admin-revenue {
background: linear-gradient(135deg,
rgba(245, 158, 11, 0.2) 0%,
rgba(245, 158, 11, 0.1) 100%);
border-color: rgba(245, 158, 11, 0.4);
}
.price-display.free {
background: linear-gradient(135deg,
rgba(139, 92, 246, 0.2) 0%,
rgba(139, 92, 246, 0.1) 100%);
border-color: rgba(139, 92, 246, 0.4);
}
.price-amount {
display: block;
font-size: 1.8rem;
font-weight: 900;
color: #48bb78;
margin-bottom: 0.3rem;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.price-display.admin-revenue .price-amount {
color: #f59e0b;
}
.price-display.free .price-amount {
color: #8b5cf6;
}
.revenue-type {
font-size: 0.8rem;
font-weight: 600;
color: #a0aec0;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Premium 10-Star Rating System */
.track-rating-section {
padding: 1.5rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.4) 0%,
rgba(15, 15, 25, 0.6) 100%);
border-top: 1px solid rgba(255, 255, 255, 0.1);
text-align: center;
}
.rating-label {
display: block;
font-size: 0.9rem;
font-weight: 700;
color: #a0aec0;
margin-bottom: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.star-rating {
display: flex;
justify-content: center;
gap: 0.3rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.star {
font-size: 1.5rem;
color: rgba(255, 255, 255, 0.2);
cursor: pointer;
transition: all 0.3s ease;
user-select: none;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
position: relative;
}
.star:hover {
transform: scale(1.2);
color: #ffd700;
text-shadow: 0 0 15px rgba(255, 215, 0, 0.8);
}
.star.filled {
color: #ffd700;
text-shadow: 0 0 10px rgba(255, 215, 0, 0.6);
}
.star.user-rated {
color: #ff6b9d;
text-shadow: 0 0 10px rgba(255, 107, 157, 0.6);
}
/* Rating hover effects */
.star-rating:hover .star {
color: rgba(255, 255, 255, 0.3);
}
.star-rating .star:hover,
.star-rating .star:hover ~ .star {
color: #ffd700;
text-shadow: 0 0 15px rgba(255, 215, 0, 0.8);
}
.rating-stats {
display: flex;
justify-content: center;
gap: 1rem;
align-items: center;
}
.avg-rating {
font-size: 1.2rem;
font-weight: 800;
color: #ffd700;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.rating-count {
font-size: 0.85rem;
color: #a0aec0;
}
/* Enhanced Track Actions */
.track-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
padding: 0;
}
.play-track-btn, .add-to-cart-btn, .share-track-btn {
padding: 1rem 1.2rem;
border-radius: 16px;
border: none;
font-weight: 700;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
font-size: 0.85rem;
text-decoration: none;
backdrop-filter: blur(10px);
position: relative;
overflow: hidden;
}
.play-track-btn i, .add-to-cart-btn i, .share-track-btn i {
font-size: 1.3rem;
}
/* Mobile Responsive Updates */
@media (max-width: 768px) {
.track-card {
border-radius: 20px;
margin-bottom: 2rem;
}
.social-actions {
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
padding: 1.5rem 1rem;
}
.social-btn {
padding: 1rem 0.8rem;
}
.social-btn i {
font-size: 1.4rem;
}
.track-actions {
grid-template-columns: 1fr;
gap: 0.8rem;
}
.star-rating {
gap: 0.2rem;
}
.star {
font-size: 1.3rem;
}
.price-amount {
font-size: 1.5rem;
}
.tech-primary {
grid-template-columns: repeat(2, 1fr);
}
.tech-secondary {
grid-template-columns: 1fr;
}
.dj-tech-grid {
padding: 1.5rem 1rem;
margin: 1rem;
}
.artist-info {
padding: 1rem;
}
.track-title {
font-size: 1.4rem;
}
.track-metadata {
grid-template-columns: repeat(2, 1fr);
padding: 1rem;
}
}
/* Premium Header Rating System */
.track-rating-header {
padding: 1.5rem;
background: linear-gradient(135deg,
rgba(255, 215, 0, 0.1) 0%,
rgba(255, 215, 0, 0.05) 100%);
border-bottom: 2px solid rgba(255, 215, 0, 0.3);
text-align: center;
position: relative;
}
.track-rating-header .star-rating {
display: flex;
justify-content: center;
gap: 0.4rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.track-rating-header .star {
font-size: 1.8rem;
color: rgba(255, 255, 255, 0.2);
cursor: pointer;
transition: all 0.3s ease;
user-select: none;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
position: relative;
}
.track-rating-header .star:hover {
transform: scale(1.3);
color: #ffd700;
text-shadow: 0 0 20px rgba(255, 215, 0, 1);
}
.track-rating-header .star.filled {
color: #ffd700;
text-shadow: 0 0 15px rgba(255, 215, 0, 0.8);
animation: starGlow 2s ease-in-out infinite alternate;
}
.track-rating-header .star.user-rated {
color: #ff6b9d;
text-shadow: 0 0 15px rgba(255, 107, 157, 0.8);
}
@keyframes starGlow {
0% { text-shadow: 0 0 15px rgba(255, 215, 0, 0.8); }
100% { text-shadow: 0 0 25px rgba(255, 215, 0, 1); }
}
/* Rating hover effects - LEFT TO RIGHT FIX */
.track-rating-header .star-rating:hover .star {
color: rgba(255, 255, 255, 0.3);
}
.track-rating-header .star-rating .star:hover ~ .star {
color: rgba(255, 255, 255, 0.3);
}
.track-rating-header .star-rating .star:hover,
.track-rating-header .star-rating .star:hover ~ .star:has(~ .star:hover) {
color: #ffd700;
text-shadow: 0 0 20px rgba(255, 215, 0, 1);
}
.track-rating-header .rating-stats {
display: flex;
justify-content: center;
gap: 1.5rem;
align-items: center;
flex-wrap: wrap;
}
.track-rating-header .avg-rating {
font-size: 1.4rem;
font-weight: 900;
color: #ffd700;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.track-rating-header .rating-count {
font-size: 0.9rem;
color: #a0aec0;
}
.chart-position {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 0.4rem 1rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
/* Enhanced Revenue Display */
.price-display.free-tier {
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.2) 0%,
rgba(118, 75, 162, 0.1) 100%);
border-color: rgba(102, 126, 234, 0.4);
}
.price-display.pro-tier {
background: linear-gradient(135deg,
rgba(72, 187, 120, 0.2) 0%,
rgba(72, 187, 120, 0.1) 100%);
border-color: rgba(72, 187, 120, 0.4);
}
.price-display.free-track {
background: linear-gradient(135deg,
rgba(139, 92, 246, 0.2) 0%,
rgba(139, 92, 246, 0.1) 100%);
border-color: rgba(139, 92, 246, 0.4);
}
.revenue-breakdown {
font-size: 0.85rem;
font-weight: 600;
color: #cbd5e0;
line-height: 1.4;
margin-top: 0.5rem;
display: block;
}
.upgrade-incentive {
display: block;
font-size: 0.8rem;
font-weight: 700;
color: #48bb78;
margin-top: 0.5rem;
padding: 0.3rem 0.8rem;
background: rgba(72, 187, 120, 0.1);
border-radius: 12px;
border: 1px solid rgba(72, 187, 120, 0.3);
}
.view-charts-btn {
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
color: white;
box-shadow: 0 4px 16px rgba(139, 92, 246, 0.3);
}
.view-charts-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.5);
}
.plan-indicator {
font-size: 0.8rem;
font-weight: 600;
color: #a0aec0;
text-transform: uppercase;
letter-spacing: 1px;
}
.premium-feature-btn {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: white;
box-shadow: 0 4px 16px rgba(245, 158, 11, 0.3);
}
.premium-feature-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(245, 158, 11, 0.5);
}
/* Chart Position as Clickable Button */
.chart-position {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 0.4rem 1rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
cursor: pointer;
transition: all 0.3s ease;
border: none;
text-decoration: none;
}
.chart-position:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
background: linear-gradient(135deg, #764ba2, #667eea);
}
/* Charts Modal */
.charts-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 1000;
backdrop-filter: blur(5px);
display: none;
align-items: center;
justify-content: center;
padding: 2rem;
}
.charts-modal-content {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 20px;
padding: 2rem;
width: 90%;
max-width: 800px;
max-height: 80vh;
overflow-y: auto;
color: white;
}
.charts-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.charts-modal h3 {
font-size: 1.8rem;
font-weight: 700;
background: linear-gradient(135deg, #ffd700, #ff6b9d);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.chart-preview {
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
padding: 1.5rem;
margin-bottom: 1.5rem;
text-align: center;
}
.current-position {
font-size: 3rem;
font-weight: 900;
color: #ffd700;
margin-bottom: 0.5rem;
}
.position-change {
font-size: 1rem;
color: #48bb78;
}
.position-change.down {
color: #f56565;
}
.view-full-charts {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
width: 100%;
margin-top: 1rem;
transition: all 0.3s ease;
}
.view-full-charts:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
/* Fixed Genres Section */
.genres-section {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
margin-bottom: 2rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.genres-section h3 {
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.4rem;
font-weight: 700;
color: #e2e8f0;
}
.genres-cloud {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
justify-content: center;
align-items: center;
}
.genre-pill {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #cbd5e0;
padding: 0.6rem 1.2rem;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
white-space: nowrap;
}
.genre-pill:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
border-color: rgba(102, 126, 234, 0.6);
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.genre-pill.active {
background: linear-gradient(135deg, #667eea, #764ba2);
border-color: #667eea;
color: white;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
/* Premium Track Actions - Glassmorphism Style */
.track-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
padding: 0;
margin-top: 1rem;
}
.play-track-btn, .add-to-cart-btn, .premium-feature-btn {
background: linear-gradient(145deg,
rgba(255, 255, 255, 0.15) 0%,
rgba(255, 255, 255, 0.05) 100%);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 16px;
padding: 1rem 1.2rem;
color: white;
font-weight: 700;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
display: flex;
flex-direction: column;
align-items: center;
gap: 0.6rem;
font-size: 0.85rem;
text-decoration: none;
position: relative;
overflow: hidden;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
.play-track-btn::before, .add-to-cart-btn::before, .premium-feature-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent,
rgba(255, 255, 255, 0.1),
transparent);
transition: left 0.6s ease;
}
.play-track-btn:hover::before, .add-to-cart-btn:hover::before, .premium-feature-btn:hover::before {
left: 100%;
}
.play-track-btn i, .add-to-cart-btn i, .premium-feature-btn i {
font-size: 1.4rem;
transition: all 0.3s ease;
}
.play-track-btn:hover, .add-to-cart-btn:hover, .premium-feature-btn:hover {
transform: translateY(-4px) scale(1.02);
border-color: rgba(255, 255, 255, 0.4);
box-shadow:
0 15px 45px rgba(0, 0, 0, 0.2),
0 8px 25px rgba(102, 126, 234, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.play-track-btn:hover i, .add-to-cart-btn:hover i, .premium-feature-btn:hover i {
transform: scale(1.1);
}
/* Individual Button Accents */
.play-track-btn {
background: linear-gradient(145deg,
rgba(102, 126, 234, 0.3) 0%,
rgba(118, 75, 162, 0.2) 100%);
border-color: rgba(102, 126, 234, 0.4);
}
.play-track-btn:hover {
background: linear-gradient(145deg,
rgba(102, 126, 234, 0.5) 0%,
rgba(118, 75, 162, 0.4) 100%);
border-color: rgba(102, 126, 234, 0.6);
box-shadow:
0 15px 45px rgba(0, 0, 0, 0.2),
0 8px 25px rgba(102, 126, 234, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.play-track-btn.playing {
background: linear-gradient(145deg,
rgba(245, 158, 11, 0.4) 0%,
rgba(217, 119, 6, 0.3) 100%);
border-color: rgba(245, 158, 11, 0.6);
animation: playingGlow 2s ease-in-out infinite alternate;
}
@keyframes playingGlow {
0% {
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
0 4px 16px rgba(245, 158, 11, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
100% {
box-shadow:
0 15px 45px rgba(0, 0, 0, 0.2),
0 8px 32px rgba(245, 158, 11, 0.7),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
}
.add-to-cart-btn {
background: linear-gradient(145deg,
rgba(72, 187, 120, 0.3) 0%,
rgba(56, 161, 105, 0.2) 100%);
border-color: rgba(72, 187, 120, 0.4);
}
.add-to-cart-btn:hover {
background: linear-gradient(145deg,
rgba(72, 187, 120, 0.5) 0%,
rgba(56, 161, 105, 0.4) 100%);
border-color: rgba(72, 187, 120, 0.6);
box-shadow:
0 15px 45px rgba(0, 0, 0, 0.2),
0 8px 25px rgba(72, 187, 120, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.premium-feature-btn {
background: linear-gradient(145deg,
rgba(245, 158, 11, 0.3) 0%,
rgba(217, 119, 6, 0.2) 100%);
border-color: rgba(245, 158, 11, 0.4);
}
.premium-feature-btn:hover {
background: linear-gradient(145deg,
rgba(245, 158, 11, 0.5) 0%,
rgba(217, 119, 6, 0.4) 100%);
border-color: rgba(245, 158, 11, 0.6);
box-shadow:
0 15px 45px rgba(0, 0, 0, 0.2),
0 8px 25px rgba(245, 158, 11, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
/* Button Loading States */
.track-actions button[disabled] {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
}
.track-actions button.loading {
pointer-events: none;
}
.track-actions button.loading i {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Mobile Responsive */
@media (max-width: 768px) {
.track-actions {
grid-template-columns: 1fr;
gap: 0.8rem;
}
.play-track-btn, .add-to-cart-btn, .premium-feature-btn {
padding: 1rem;
flex-direction: row;
justify-content: center;
}
.play-track-btn i, .add-to-cart-btn i, .premium-feature-btn i {
font-size: 1.2rem;
}
}
/* Premium Lyrics Section */
.track-lyrics-section {
margin: 1.5rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.4) 0%,
rgba(15, 15, 25, 0.6) 100%);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.1);
overflow: hidden;
backdrop-filter: blur(10px);
}
.lyrics-toggle {
width: 100%;
background: transparent;
border: none;
color: white;
padding: 1rem 1.5rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 1rem;
font-size: 0.95rem;
font-weight: 600;
transition: all 0.3s ease;
border-radius: 16px;
}
.lyrics-toggle:hover {
background: rgba(255, 255, 255, 0.05);
}
.lyrics-toggle span {
flex: 1;
text-align: left;
}
.toggle-icon {
transition: transform 0.3s ease;
color: #667eea;
}
.lyrics-toggle.active .toggle-icon {
transform: rotate(180deg);
}
.lyrics-toggle.active span {
color: #667eea;
}
.lyrics-content {
border-top: 1px solid rgba(255, 255, 255, 0.1);
max-height: 0;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.lyrics-content.expanded {
max-height: 400px;
overflow-y: auto;
}
.lyrics-text {
padding: 1.5rem;
line-height: 1.8;
color: #cbd5e0;
font-size: 0.95rem;
white-space: pre-line;
font-family: 'Georgia', serif;
text-align: center;
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.02) 0%,
rgba(255, 255, 255, 0.05) 100%);
position: relative;
}
.lyrics-text::before {
content: '"';
position: absolute;
top: 0.5rem;
left: 1rem;
font-size: 3rem;
color: rgba(102, 126, 234, 0.3);
font-weight: bold;
line-height: 1;
}
.lyrics-text::after {
content: '"';
position: absolute;
bottom: 0.5rem;
right: 1rem;
font-size: 3rem;
color: rgba(102, 126, 234, 0.3);
font-weight: bold;
line-height: 1;
}
/* COMPACT TRACK CARDS - 50% SMALLER */
.track-card.compact-card {
background: linear-gradient(145deg,
rgba(15, 15, 35, 0.95) 0%,
rgba(25, 25, 45, 0.9) 50%,
rgba(35, 35, 55, 0.85) 100%);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 16px;
padding: 1.2rem;
margin-bottom: 1rem;
max-width: 320px;
transition: all 0.3s ease;
backdrop-filter: blur(20px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}
.track-card.compact-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.4);
}
/* Track Number Badge */
.track-number-badge {
position: absolute;
top: 0.8rem;
right: 0.8rem;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
font-size: 0.75rem;
font-weight: 700;
padding: 0.25rem 0.4rem;
border-radius: 6px;
z-index: 10;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
min-width: 1.5rem;
text-align: center;
line-height: 1;
}
/* Compact Header */
.compact-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.artist-mini {
display: flex;
align-items: center;
gap: 0.8rem;
flex: 1;
}
.artist-avatar-mini {
width: 32px;
height: 32px;
border-radius: 8px;
overflow: hidden;
}
.artist-avatar-mini img,
.default-avatar-mini {
width: 100%;
height: 100%;
object-fit: cover;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
border-radius: 8px;
}
.artist-info-mini {
flex: 1;
min-width: 0;
}
.track-title-mini {
color: white;
font-size: 1rem;
font-weight: 700;
margin-bottom: 0.2rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.artist-name-mini {
color: #a0aec0;
font-size: 0.8rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Quick Actions */
.quick-actions {
display: flex;
align-items: center;
gap: 0.3rem;
flex-wrap: wrap;
margin-top: 0.5rem;
}
.compact-actions {
display: flex;
align-items: center;
gap: 0.8rem;
margin-top: 1rem;
justify-content: space-between;
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.08) 0%,
rgba(118, 75, 162, 0.08) 100%);
padding: 1rem;
border-radius: 16px;
border: 1px solid rgba(102, 126, 234, 0.15);
position: relative;
z-index: 2;
backdrop-filter: blur(15px);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.action-btn-compact {
width: 36px;
height: 36px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.2) 0%,
rgba(118, 75, 162, 0.2) 100%);
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.9rem;
position: relative;
overflow: hidden;
backdrop-filter: blur(10px);
}
.action-btn-compact:hover {
transform: translateY(-3px) scale(1.05);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
border-color: rgba(102, 126, 234, 0.4);
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.3) 0%,
rgba(118, 75, 162, 0.3) 100%);
}
.action-btn-compact::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.6s ease;
}
.action-btn-compact:hover::before {
left: 100%;
}
.action-btn-compact.primary {
background: linear-gradient(135deg,
rgba(72, 187, 120, 0.3) 0%,
rgba(56, 161, 105, 0.3) 100%);
border-color: rgba(72, 187, 120, 0.4);
}
.action-btn-compact.primary:hover {
background: linear-gradient(135deg,
rgba(72, 187, 120, 0.4) 0%,
rgba(56, 161, 105, 0.4) 100%);
border-color: rgba(72, 187, 120, 0.6);
box-shadow: 0 8px 25px rgba(72, 187, 120, 0.3);
}
.action-btn-compact.danger {
background: linear-gradient(135deg,
rgba(245, 101, 101, 0.3) 0%,
rgba(229, 62, 62, 0.3) 100%);
border-color: rgba(245, 101, 101, 0.4);
}
.action-btn-compact.danger:hover {
background: linear-gradient(135deg,
rgba(245, 101, 101, 0.4) 0%,
rgba(229, 62, 62, 0.4) 100%);
border-color: rgba(245, 101, 101, 0.6);
box-shadow: 0 8px 25px rgba(245, 101, 101, 0.3);
}
.action-btn-compact i {
font-size: 0.9rem;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
}
.action-btn-compact[title]:hover::after {
content: attr(title);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 0.3rem 0.6rem;
border-radius: 4px;
font-size: 0.7rem;
white-space: nowrap;
z-index: 1000;
margin-bottom: 0.3rem;
}
/* Enhanced button integration */
.track-card.compact-card .compact-actions {
border-top: 1px solid rgba(102, 126, 234, 0.1);
margin-top: 1rem;
padding-top: 0.8rem;
background: linear-gradient(90deg,
rgba(102, 126, 234, 0.05) 0%,
rgba(118, 75, 162, 0.05) 100%);
}
.track-card.compact-card:hover .compact-actions {
background: linear-gradient(90deg,
rgba(102, 126, 234, 0.1) 0%,
rgba(118, 75, 162, 0.1) 100%);
border-top-color: rgba(102, 126, 234, 0.2);
}
/* Mobile responsive for compact buttons */
@media (max-width: 768px) {
.compact-actions {
gap: 0.4rem;
padding: 0.6rem;
}
.action-btn-compact {
width: 28px;
height: 28px;
font-size: 0.8rem;
}
.action-btn-compact i {
font-size: 0.8rem;
}
}
@media (max-width: 480px) {
.compact-actions {
gap: 0.5rem;
padding: 0.7rem;
justify-content: center;
}
.action-btn-compact {
width: 32px;
height: 32px;
font-size: 0.9rem;
}
.action-btn-compact i {
font-size: 0.9rem;
}
}
.action-btn {
width: 28px;
height: 28px;
border-radius: 6px;
border: none;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.8rem;
position: relative;
}
.action-btn:hover {
transform: scale(1.1);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.action-btn.play-btn.playing,
.action-btn-compact.primary.playing {
background: linear-gradient(135deg, #48bb78, #38a169);
animation: playingPulse 1.5s ease-in-out infinite;
}
.track-card.playing {
border-color: rgba(67, 233, 123, 0.6);
box-shadow: 0 8px 30px rgba(67, 233, 123, 0.3);
}
@keyframes playingPulse {
0%, 100% {
box-shadow: 0 4px 15px rgba(72, 187, 120, 0.4);
transform: scale(1);
}
50% {
box-shadow: 0 8px 25px rgba(72, 187, 120, 0.7);
transform: scale(1.05);
}
}
.price-mini {
font-size: 0.8rem;
color: #ffd700;
font-weight: 600;
white-space: nowrap;
}
/* Compact DJ Tech Grid */
.dj-tech-compact {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.8rem;
margin-bottom: 1.2rem;
padding: 1.2rem;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.2) 0%,
rgba(15, 15, 35, 0.3) 100%);
border-radius: 16px;
border: 1px solid rgba(102, 126, 234, 0.15);
backdrop-filter: blur(20px);
position: relative;
overflow: hidden;
}
.dj-tech-compact::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg,
transparent 0%,
rgba(102, 126, 234, 0.4) 50%,
transparent 100%);
}
.dj-tech-compact:hover {
border-color: rgba(102, 126, 234, 0.3);
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.1);
}
.tech-item {
text-align: center;
padding: 0.8rem 0.6rem;
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.1) 0%,
rgba(118, 75, 162, 0.1) 100%);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 12px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
backdrop-filter: blur(10px);
}
.tech-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg,
rgba(102, 126, 234, 0.8) 0%,
rgba(118, 75, 162, 0.8) 100%);
transform: scaleX(0);
transition: transform 0.3s ease;
}
.tech-item:hover {
background: linear-gradient(135deg,
rgba(102, 126, 234, 0.15) 0%,
rgba(118, 75, 162, 0.15) 100%);
border-color: rgba(102, 126, 234, 0.4);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2);
}
.tech-item:hover::before {
transform: scaleX(1);
}
.tech-label {
font-size: 0.65rem;
color: #a0aec0;
display: block;
margin-bottom: 0.3rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.tech-value {
font-size: 0.9rem;
font-weight: 800;
color: white;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.tech-value.bpm-value {
color: #ff6b6b;
background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.tech-value.key-value {
color: #4ecdc4;
background: linear-gradient(135deg, #4ecdc4, #6ee7df);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.tech-value.mood-value {
color: #feca57;
background: linear-gradient(135deg, #feca57, #ffd93d);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.tech-value.energy-value {
color: #ff9ff3;
background: linear-gradient(135deg, #ff9ff3, #ffb3f3);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Clickable Genres */
.track-genres {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.genre-tag {
padding: 0.3rem 0.8rem;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 12px;
font-size: 0.7rem;
color: #667eea;
cursor: pointer;
transition: all 0.3s ease;
}
.genre-tag:hover {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
transform: scale(1.05);
color: white;
}
/* Compact Waveform */
.waveform-compact {
height: 40px;
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
padding: 0.5rem;
display: flex;
align-items: end;
gap: 2px;
cursor: pointer;
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.waveform-compact:hover {
background: rgba(102, 126, 234, 0.1);
}
.wave-bar {
flex: 1;
background: linear-gradient(to top, #667eea, #764ba2);
border-radius: 1px;
min-height: 8px;
transition: all 0.3s ease;
}
.waveform-compact:hover .wave-bar {
background: linear-gradient(to top, #5a67d8, #6b46c1);
}
/* Bottom Section */
.bottom-section {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.track-stats {
display: flex;
gap: 0.8rem;
font-size: 0.8rem;
color: #a0aec0;
}
.track-stats span {
display: flex;
align-items: center;
gap: 0.3rem;
}
.track-stats i {
font-size: 0.7rem;
}
/* Compact Rating */
.rating-compact {
display: flex;
align-items: center;
gap: 0.5rem;
}
.star-rating-mini {
display: flex;
gap: 2px;
}
.star-mini {
font-size: 0.9rem;
color: #4a5568;
cursor: pointer;
transition: all 0.2s ease;
}
.star-mini.filled {
color: #ffd700;
}
.star-mini:hover {
color: #ffd700;
transform: scale(1.2);
}
.rating-text {
font-size: 0.8rem;
color: #a0aec0;
font-weight: 600;
}
/* Compact Social Actions */
.social-compact {
display: flex;
gap: 0.3rem;
}
.social-btn-mini {
width: 24px;
height: 24px;
border: none;
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.8rem;
}
.social-btn-mini:hover {
background: rgba(102, 126, 234, 0.2);
color: white;
transform: scale(1.1);
}
.social-btn-mini.like-btn.liked {
background: linear-gradient(135deg, #ff6b9d, #c44569);
color: white;
}
/* Pagination Controls */
.pagination-container {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
margin: 3rem 0;
padding: 2rem;
}
.pagination-info {
color: #a0aec0;
font-size: 1.4rem;
}
.pagination-controls {
display: flex;
gap: 0.5rem;
align-items: center;
}
/* Grid Layout for Compact Cards */
.tracks-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
padding: 0 1rem;
justify-items: stretch;
align-items: start;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
/* Fix crooked alignment */
grid-auto-rows: 1fr;
align-content: start;
}
/* Ensure cards are properly aligned */
.track-card.compact-card {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
margin: 0;
box-sizing: border-box;
}
/* Fix any remaining alignment issues */
.tracks-grid > * {
margin: 0;
box-sizing: border-box;
}
/* Ensure proper grid alignment */
.tracks-grid {
place-items: start stretch;
}
/* Fix card content alignment */
.track-card.compact-card > * {
width: 100%;
}
/* Share Modal Styles */
.share-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(5px);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.share-modal.show {
opacity: 1;
}
.share-modal-content {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(25px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 20px;
padding: 2rem;
width: 90%;
max-width: 500px;
color: white;
animation: modalSlideIn 0.3s ease;
}
@keyframes modalSlideIn {
from {
transform: translateY(-50px) scale(0.9);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
.share-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.share-header h3 {
margin: 0;
font-size: 1.5rem;
}
.close-modal {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 0.5rem;
border-radius: 50%;
transition: all 0.3s ease;
}
.close-modal:hover {
background: rgba(255, 255, 255, 0.1);
}
.share-options {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.share-option {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 1rem;
color: white;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.8rem;
font-size: 1rem;
}
.share-option:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateY(-2px);
}
.share-option i {
font-size: 1.2rem;
}
/* Mobile Responsiveness */
@media (max-width: 768px) {
.tracks-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
padding: 0 0.5rem;
place-items: center;
}
.track-card.compact-card {
max-width: 100%;
width: 100%;
height: auto;
}
}
.dj-tech-compact {
grid-template-columns: repeat(2, 1fr);
gap: 0.3rem;
}
.bottom-section {
flex-direction: column;
gap: 0.8rem;
}
.share-options {
grid-template-columns: 1fr;
}
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
z-index: 10000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fff;
margin: 5% auto;
padding: 2rem;
border-radius: 12px;
width: 90%;
max-width: 500px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: #333;
}
.form-input {
width: 100%;
padding: 0.75rem;
border: 2px solid #e1e5e9;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.3s ease;
}
.form-input:focus {
outline: none;
border-color: #667eea;
}
.form-actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
margin-top: 2rem;
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5a67d8;
}
.btn:not(.btn-primary) {
background: #f8f9fa;
color: #333;
}
.btn:not(.btn-primary):hover {
background: #e9ecef;
}
</style>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<div class="hero-badge">
<i class="fas fa-music"></i>
Your Music Library
</div>
<h1 class="hero-title">Your Studio Collection</h1>
<p class="hero-subtitle">Manage, play, and share your AI-generated tracks. Every creation is a masterpiece waiting to be discovered.</p>
</div>
<div class="hero-visual">
<div class="floating-elements">
<div class="floating-note">βͺ</div>
<div class="floating-note">β«</div>
<div class="floating-note">β¬</div>
<div class="floating-note">π΅</div>
<div class="floating-note">πΆ</div>
</div>
</div>
</div>
</section>
<!-- Success/Error Messages -->
<?php if (isset($_SESSION['success_message'])): ?>
<div class="alert alert-success" style="margin: 1rem; padding: 1rem; background: #d4edda; color: #155724; border: 1px solid #c3e6cb; border-radius: 8px;">
<?= htmlspecialchars($_SESSION['success_message']) ?>
</div>
<?php unset($_SESSION['success_message']); ?>
<?php endif; ?>
<?php if (isset($_SESSION['error_message'])): ?>
<div class="alert alert-error" style="margin: 1rem; padding: 1rem; background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; border-radius: 8px;">
<?= htmlspecialchars($_SESSION['error_message']) ?>
</div>
<?php unset($_SESSION['error_message']); ?>
<?php endif; ?>
<!-- Community Content -->
<section class="community-content">
<div class="container">
<!-- Library Stats -->
<div class="community-stats">
<div class="stat-card">
<div class="stat-number"><?= $user_stats['total_tracks'] ?></div>
<div class="stat-label">Total Tracks</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= $user_stats['completed_tracks'] ?></div>
<div class="stat-label">Completed</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= $user_stats['processing_tracks'] ?></div>
<div class="stat-label">Processing</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= $credits ?></div>
<div class="stat-label">Credits Left</div>
</div>
</div>
<!-- Unified Filter Controls -->
<div class="unified-filters">
<div class="filter-row">
<div class="filter-group">
<label class="filter-label">Status:</label>
<select name="status" class="filter-select">
<option value="all" <?= $status_filter === 'all' ? 'selected' : '' ?>>All Tracks</option>
<option value="complete" <?= $status_filter === 'complete' ? 'selected' : '' ?>>Completed</option>
<option value="processing" <?= $status_filter === 'processing' ? 'selected' : '' ?>>Processing</option>
<option value="failed" <?= $status_filter === 'failed' ? 'selected' : '' ?>>Failed</option>
</select>
</div>
<div class="filter-group">
<label class="filter-label">Sort By:</label>
<select name="sort" class="filter-select">
<option value="latest" <?= $sort_filter === 'latest' ? 'selected' : '' ?>>Latest First</option>
<option value="oldest" <?= $sort_filter === 'oldest' ? 'selected' : '' ?>>Oldest First</option>
<option value="popular" <?= $sort_filter === 'popular' ? 'selected' : '' ?>>Most Popular</option>
<option value="most-played" <?= $sort_filter === 'most-played' ? 'selected' : '' ?>>Most Played</option>
</select>
</div>
<div class="filter-group">
<label class="filter-label">Time:</label>
<select name="time" class="filter-select">
<option value="all" <?= $time_filter === 'all' ? 'selected' : '' ?>>All Time</option>
<option value="today" <?= $time_filter === 'today' ? 'selected' : '' ?>>Today</option>
<option value="week" <?= $time_filter === 'week' ? 'selected' : '' ?>>This Week</option>
<option value="month" <?= $time_filter === 'month' ? 'selected' : '' ?>>This Month</option>
</select>
</div>
<div class="filter-group">
<label class="filter-label">Genre:</label>
<select name="genre" class="filter-select">
<option value="">All Genres (<?= count($available_genres) ?>)</option>
<?php foreach ($available_genres as $genre): ?>
<option value="<?= htmlspecialchars($genre) ?>" <?= $genre_filter === $genre ? 'selected' : '' ?>>
<?= htmlspecialchars($genre) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<!-- Tracks Grid -->
<div class="tracks-grid">
<?php if (empty($recent_tracks)): ?>
<div style="grid-column: 1 / -1; text-align: center; padding: 4rem;">
<h3>No tracks found</h3>
<p>Be the first to create amazing AI music!</p>
</div>
<?php else: ?>
<!-- Library Tracks Grid -->
<div class="tracks-grid">
<?php if (empty($tracks_with_variations)): ?>
<div class="empty-state">
<div class="empty-icon">π΅</div>
<h2 class="empty-title">No Tracks Yet</h2>
<p class="empty-description">
Start creating your first AI-generated track and build your music library.
</p>
<a href="/#create" class="create-first-btn">
<i class="fas fa-plus"></i>
Create Your First Track
</a>
</div>
<?php else: ?>
<?php
// Count total tracks first to get the correct numbering
$total_tracks = count($tracks_with_variations);
$track_number = $total_tracks; // Start from the oldest track (highest number)
foreach ($tracks_with_variations as $track):
$displayTitle = $track['title'] ?: 'Untitled Track';
$duration = floor($track['duration'] / 60) . 'm ' . ($track['duration'] % 60) . 's';
$created_date = date('M j, Y', strtotime($track['created_at']));
$error_message = $track['error_message'] ?? null;
?>
<div class="track-card compact-card" data-track-id="<?= $track['id'] ?>" data-status="<?= $track['status'] ?>" data-variations="<?= htmlspecialchars(json_encode($track['variations'] ?? [])) ?>" data-selected-variation="<?= $track['selected_variation_index'] ?? 0 ?>">
<!-- Track Number Badge -->
<div class="track-number-badge">
#<?= $track_number ?>
</div>
<!-- Waveform Visualizer -->
<div class="waveform-visualizer"></div>
<!-- Track Header -->
<div class="compact-header">
<div class="artist-mini">
<div class="artist-avatar-mini">
<div class="default-avatar-mini"><?= substr(htmlspecialchars($user_name), 0, 1) ?></div>
</div>
<div class="artist-info-mini">
<div class="track-title-mini"><?= htmlspecialchars($displayTitle) ?></div>
<div class="artist-name-mini">by <?= htmlspecialchars($user_name) ?></div>
</div>
</div>
<!-- Status Badge -->
<?php if ($track['badge']): ?>
<div class="track-badge"><?= $track['badge'] ?></div>
<?php endif; ?>
<!-- Status Indicator -->
<div class="status-indicator">
<?php if ($track['status'] === 'complete'): ?>
<span class="status-complete">Complete</span>
<?php elseif ($track['status'] === 'processing'): ?>
<span class="status-processing">Processing</span>
<?php elseif ($track['status'] === 'failed'): ?>
<span class="status-failed">Failed</span>
<?php endif; ?>
</div>
</div>
<!-- Track Prompt -->
<?php if ($track['prompt']): ?>
<div class="track-prompt">
<?= htmlspecialchars($track['prompt']) ?>
</div>
<?php endif; ?>
<!-- Error Message for Failed Tracks -->
<?php if ($track['status'] === 'failed' && $error_message): ?>
<div class="error-message-modern">
<div class="error-header">
<i class="fas fa-exclamation-triangle"></i>
<strong>Error Details:</strong>
</div>
<div class="error-content">
<?= htmlspecialchars($error_message) ?>
</div>
<div class="error-tip">
<i class="fas fa-lightbulb"></i>
<strong>Quick Fix:</strong>
<?php if (stripos($error_message, 'artist name') !== false): ?>
Avoid mentioning existing artists or bands. Use music genres and styles instead.
<?php elseif (stripos($error_message, 'copyright') !== false): ?>
Don't reference existing songs, lyrics, or copyrighted material.
<?php elseif (stripos($error_message, 'inappropriate') !== false): ?>
Keep content family-friendly and appropriate.
<?php else: ?>
Try modifying your prompt to be more generic and avoid specific names.
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- Enhanced Track Metadata (like community_fixed.php) -->
<?php
// Parse metadata from JSON if it exists
$metadata = json_decode($track['metadata'] ?? '{}', true) ?: [];
$genre = $metadata['genre'] ?? 'Electronic';
$bpm = $metadata['bpm'] ?? 120;
$key = $metadata['key'] ?? 'C major';
$time_signature = $metadata['time_signature'] ?? '4/4';
$instruments = is_array($metadata['instruments'] ?? null) ? implode(', ', $metadata['instruments']) : ($metadata['instruments'] ?? 'Synthesizer');
$mood = $metadata['mood'] ?? 'Neutral';
$energy = $metadata['energy'] ?? 'Medium';
// Parse generation parameters
$generationParams = json_decode($track['generation_parameters'] ?? '{}', true) ?: [];
$modelVersion = $generationParams['model_version'] ?? null;
$modelName = $generationParams['model_name'] ?? null;
$temperature = $generationParams['temperature'] ?? null;
$seed = $generationParams['seed'] ?? null;
$topP = $generationParams['top_p'] ?? null;
$topK = $generationParams['top_k'] ?? null;
$maxTokens = $generationParams['max_tokens'] ?? null;
// Parse processing info
$processingInfo = json_decode($track['processing_info'] ?? '{}', true) ?: [];
$processingTime = $processingInfo['processing_time'] ?? null;
$queueTime = $processingInfo['queue_time'] ?? null;
$totalTime = $processingInfo['total_time'] ?? null;
// Parse cost info
$costInfo = json_decode($track['cost_info'] ?? '{}', true) ?: [];
$apiCost = $costInfo['api_cost'] ?? null;
$creditsUsed = $costInfo['credits_used'] ?? null;
$currency = $costInfo['currency'] ?? 'USD';
// Parse audio quality
$audioQuality = json_decode($track['audio_quality'] ?? '{}', true) ?: [];
$bitrate = $audioQuality['bitrate'] ?? null;
$sampleRate = $audioQuality['sample_rate'] ?? null;
$format = $audioQuality['format'] ?? 'mp3';
$fileSize = $audioQuality['file_size'] ?? null;
$qualityScore = $audioQuality['audio_quality_score'] ?? null;
// Parse audio analysis
$audioAnalysis = json_decode($track['audio_analysis'] ?? '{}', true) ?: [];
$loudness = $audioAnalysis['loudness'] ?? null;
$dynamicRange = $audioAnalysis['dynamic_range'] ?? null;
$peakAmplitude = $audioAnalysis['peak_amplitude'] ?? null;
// Get variation metadata if available
$selectedVariationIndex = $track['selected_variation_index'] ?? 0;
$variations = $track['variations'] ?? [];
$selectedVariation = $variations[$selectedVariationIndex] ?? $variations[0] ?? null;
if ($selectedVariation) {
$variationMetadata = json_decode($selectedVariation['metadata'] ?? '{}', true);
$genre = $variationMetadata['genre'] ?? $genre;
$bpm = $variationMetadata['bpm'] ?? $bpm;
$key = $variationMetadata['key'] ?? $key;
$time_signature = $variationMetadata['time_signature'] ?? $time_signature;
$mood = $variationMetadata['mood'] ?? $mood;
$energy = $variationMetadata['energy'] ?? $energy;
$instruments = is_array($variationMetadata['instruments'] ?? null) ? implode(', ', $variationMetadata['instruments']) : ($variationMetadata['instruments'] ?? $instruments ?? 'Synthesizer');
}
// Format values
$formattedProcessingTime = $processingTime ? formatTime($processingTime) : null;
$formattedCost = $apiCost ? '$' . number_format($apiCost, 4) : null;
$formattedFileSize = $fileSize ? formatBytes($fileSize) : null;
// Debug: Log generation parameters for troubleshooting
if (strtotime($track['created_at']) > strtotime('-1 hour')) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("π΅ Library Debug - Track {$track['id']} Generation Params:");
error_log(" - Model: " . ($modelName ?? 'NULL'));
error_log(" - Temperature: " . ($temperature ?? 'NULL'));
error_log(" - Processing Time: " . ($processingTime ?? 'NULL'));
error_log(" - API Cost: " . ($apiCost ?? 'NULL'));
error_log(" - File Size: " . ($fileSize ?? 'NULL'));
}
}
?>
<!-- DJ Tech Grid (like community_fixed.php) -->
<div class="dj-tech-compact">
<div class="tech-item">
<span class="tech-label">BPM</span>
<span class="tech-value bpm-value"><?= $bpm ?: '120' ?></span>
</div>
<div class="tech-item">
<span class="tech-label">KEY</span>
<span class="tech-value key-value"><?= $key ?: 'C maj' ?></span>
</div>
<div class="tech-item">
<span class="tech-label">MOOD</span>
<span class="tech-value mood-value"><?= $mood ?: 'Neutral' ?></span>
</div>
<div class="tech-item">
<span class="tech-label">ENERGY</span>
<span class="tech-value energy-value"><?= $energy ?: 'Medium' ?></span>
</div>
</div>
<!-- Comprehensive Generation Parameters -->
<?php if ($modelName || $temperature || $seed || $processingTime || $apiCost || $formattedFileSize || $bitrate || $sampleRate || $loudness || $dynamicRange || $peakAmplitude): ?>
<div class="generation-params">
<div class="params-header" onclick="toggleGenerationParams(this)">
<i class="fas fa-cogs"></i>
<span>Generation Parameters</span>
<i class="fas fa-chevron-down toggle-icon"></i>
</div>
<div class="params-grid" style="display: none;">
<?php if ($modelName): ?>
<div class="param-item">
<span class="param-label">Model</span>
<span class="param-value"><?= htmlspecialchars($modelName) ?></span>
</div>
<?php endif; ?>
<?php if ($modelVersion): ?>
<div class="param-item">
<span class="param-label">Version</span>
<span class="param-value"><?= htmlspecialchars($modelVersion) ?></span>
</div>
<?php endif; ?>
<?php if ($temperature): ?>
<div class="param-item">
<span class="param-label">Temperature</span>
<span class="param-value"><?= number_format($temperature, 2) ?></span>
</div>
<?php endif; ?>
<?php if ($seed): ?>
<div class="param-item">
<span class="param-label">Seed</span>
<span class="param-value"><?= htmlspecialchars($seed) ?></span>
</div>
<?php endif; ?>
<?php if ($topP): ?>
<div class="param-item">
<span class="param-label">Top P</span>
<span class="param-value"><?= number_format($topP, 2) ?></span>
</div>
<?php endif; ?>
<?php if ($topK): ?>
<div class="param-item">
<span class="param-label">Top K</span>
<span class="param-value"><?= htmlspecialchars($topK) ?></span>
</div>
<?php endif; ?>
<?php if ($maxTokens): ?>
<div class="param-item">
<span class="param-label">Max Tokens</span>
<span class="param-value"><?= number_format($maxTokens) ?></span>
</div>
<?php endif; ?>
<?php if ($formattedProcessingTime): ?>
<div class="param-item">
<span class="param-label">Processing</span>
<span class="param-value"><?= $formattedProcessingTime ?></span>
</div>
<?php endif; ?>
<?php if ($formattedCost): ?>
<div class="param-item">
<span class="param-label">API Cost</span>
<span class="param-value"><?= $formattedCost ?></span>
</div>
<?php endif; ?>
<?php if ($creditsUsed): ?>
<div class="param-item">
<span class="param-label">Credits</span>
<span class="param-value"><?= $creditsUsed ?></span>
</div>
<?php endif; ?>
<?php if ($formattedFileSize): ?>
<div class="param-item">
<span class="param-label">File Size</span>
<span class="param-value"><?= $formattedFileSize ?></span>
</div>
<?php endif; ?>
<?php if ($bitrate): ?>
<div class="param-item">
<span class="param-label">Bitrate</span>
<span class="param-value"><?= number_format($bitrate) ?> kbps</span>
</div>
<?php endif; ?>
<?php if ($sampleRate): ?>
<div class="param-item">
<span class="param-label">Sample Rate</span>
<span class="param-value"><?= number_format($sampleRate) ?> Hz</span>
</div>
<?php endif; ?>
<?php if ($loudness): ?>
<div class="param-item">
<span class="param-label">Loudness</span>
<span class="param-value"><?= number_format($loudness, 1) ?> dB</span>
</div>
<?php endif; ?>
<?php if ($dynamicRange): ?>
<div class="param-item">
<span class="param-label">Dynamic Range</span>
<span class="param-value"><?= number_format($dynamicRange, 1) ?> dB</span>
</div>
<?php endif; ?>
<?php if ($peakAmplitude): ?>
<div class="param-item">
<span class="param-label">Peak</span>
<span class="param-value"><?= round($peakAmplitude * 100) ?>%</span>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- Clickable Waveform (like community_fixed.php) -->
<?php if ($track['status'] === 'complete'): ?>
<?php
// Get the correct audio URL - use selected variation if available, otherwise use main track
$audioUrl = $track['audio_url'];
if ($selectedVariation && !empty($selectedVariation['audio_url'])) {
$audioUrl = $selectedVariation['audio_url'];
}
// Generate waveform bars - use actual waveform data if available, otherwise random
$waveformBars = [];
$waveformData = json_decode($track['waveform_data'] ?? '{}', true);
if (isset($waveformData['waveform']) && is_array($waveformData['waveform'])) {
$waveformBars = array_slice($waveformData['waveform'], 0, 25);
} else {
// Generate random waveform bars
for ($i = 0; $i < 25; $i++) {
$waveformBars[] = rand(20, 100);
}
}
?>
<div class="waveform-compact" onclick="playTrackFromWaveform(<?= $track['id'] ?>, '<?= htmlspecialchars($audioUrl) ?>', '<?= htmlspecialchars($displayTitle) ?>', '<?= htmlspecialchars($user_name) ?>')">
<?php foreach ($waveformBars as $barHeight): ?>
<div class="wave-bar" style="height: <?= $barHeight ?>%;"></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- Enhanced Genre Tags with Expandable Feature -->
<div class="track-genres">
<?php
$genres = [];
if (isset($metadata['genres']) && is_array($metadata['genres'])) {
$genres = $metadata['genres'];
} elseif (isset($metadata['genre'])) {
$genres = [$metadata['genre']];
}
// Add variation genres if available
if ($selectedVariation) {
$variationGenres = json_decode($selectedVariation['metadata'] ?? '{}', true);
if (isset($variationGenres['genres']) && is_array($variationGenres['genres'])) {
$genres = array_merge($genres, $variationGenres['genres']);
} elseif (isset($variationGenres['genre'])) {
$genres[] = $variationGenres['genre'];
}
}
$genres = array_unique($genres);
$displayGenres = array_slice($genres, 0, 3);
$hiddenGenres = array_slice($genres, 3);
?>
<?php foreach ($displayGenres as $trackGenre): ?>
<span class="genre-tag"><?= htmlspecialchars($trackGenre) ?></span>
<?php endforeach; ?>
<?php if (!empty($hiddenGenres)): ?>
<span class="genre-tag expand-btn" onclick="toggleGenreExpansion(this, <?= htmlspecialchars(json_encode($hiddenGenres)) ?>)">
+<?= count($hiddenGenres) ?> more
</span>
<div class="expanded-genres" style="display: none;">
<?php foreach ($hiddenGenres as $hiddenGenre): ?>
<span class="genre-tag"><?= htmlspecialchars($hiddenGenre) ?></span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- Basic Track Info -->
<div class="track-metadata">
<div class="metadata-item">
<div class="metadata-label">Duration</div>
<div class="metadata-value"><?= $duration ?></div>
</div>
<div class="metadata-item">
<div class="metadata-label">Created</div>
<div class="metadata-value"><?= $created_date ?></div>
</div>
<?php if ($track['variation_count'] > 0): ?>
<div class="metadata-item">
<div class="metadata-label">Variations</div>
<div class="metadata-value"><?= $track['variation_count'] ?></div>
</div>
<?php endif; ?>
<?php if ($instruments): ?>
<div class="metadata-item">
<div class="metadata-label">Instruments</div>
<div class="metadata-value"><?= htmlspecialchars($instruments) ?></div>
</div>
<?php endif; ?>
</div>
<!-- Track Actions - Integrated within card -->
<div class="compact-actions">
<?php if ($track['status'] === 'complete'): ?>
<?php
// Get the correct audio URL - use selected variation if available, otherwise use main track
$audioUrl = $track['audio_url'];
if ($selectedVariation && !empty($selectedVariation['audio_url'])) {
$audioUrl = $selectedVariation['audio_url'];
}
// Debug logging for new tracks
if (strtotime($track['created_at']) > strtotime('-1 hour')) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
error_log("π΅ Library Debug - New Track {$track['id']}:");
error_log(" - Main audio_url: " . ($track['audio_url'] ?? 'NULL'));
error_log(" - Selected variation audio_url: " . ($selectedVariation['audio_url'] ?? 'NULL'));
error_log(" - Final audioUrl: " . $audioUrl);
error_log(" - Track status: " . $track['status']);
}
}
?>
<div class="compact-actions">
<button class="action-btn-compact primary" onclick="playTrackFromButton('<?= htmlspecialchars($audioUrl) ?>', '<?= htmlspecialchars($displayTitle) ?>', '<?= htmlspecialchars($user_name) ?>')"
data-audio-url="<?= htmlspecialchars($audioUrl) ?>"
data-title="<?= htmlspecialchars($displayTitle) ?>"
data-artist="<?= htmlspecialchars($user_name) ?>"
data-track-id="<?= $track['id'] ?>"
title="Play Track">
<i class="fas fa-play"></i>
</button>
<button class="action-btn-compact" onclick="downloadTrack(<?= $track['id'] ?>)" title="Download Track">
<i class="fas fa-download"></i>
</button>
<?php if ($track['variation_count'] > 0): ?>
<button class="action-btn-compact" onclick="showVariations(<?= $track['id'] ?>)" title="View Variations">
<i class="fas fa-layer-group"></i>
</button>
<?php endif; ?>
<?php if ($track['user_id'] == $_SESSION['user_id']): ?>
<button class="action-btn-compact" onclick="editTrack(<?= $track['id'] ?>, '<?= htmlspecialchars(addslashes($track['title'])) ?>', '<?= htmlspecialchars(addslashes($track['description'] ?? '')) ?>', '<?= htmlspecialchars($track['price'] ?? '0') ?>', <?= $track['is_public'] ?? 0 ?>)" title="Edit Track">
<i class="fas fa-edit"></i>
</button>
<?php endif; ?>
<button class="action-btn-compact" onclick="showLyrics(<?= $track['id'] ?>)" title="View Lyrics">
<i class="fas fa-music"></i>
</button>
</div>
<?php elseif ($track['status'] === 'processing'): ?>
<div class="compact-actions">
<button class="action-btn-compact" onclick="checkTrackStatus(<?= $track['id'] ?>)" title="Check Status">
<i class="fas fa-sync-alt"></i>
</button>
</div>
<?php elseif ($track['status'] === 'failed'): ?>
<div class="compact-actions">
<button class="action-btn-compact primary" onclick="retryTrack(<?= $track['id'] ?>)" title="Retry Track">
<i class="fas fa-redo"></i>
</button>
<button class="action-btn-compact" onclick="showFailureHelp(<?= $track['id'] ?>)" title="Why did this fail?">
<i class="fas fa-question-circle"></i>
</button>
<button class="action-btn-compact danger" onclick="deleteFailedTrack(<?= $track['id'] ?>)" title="Delete this failed track">
<i class="fas fa-trash"></i>
</button>
</div>
<?php endif; ?>
</div>
</div>
<?php $track_number--; // Decrement track number for next track (oldest to newest) ?>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
</section>
</div>
<script>
console.log('π΅ Community Fixed - JavaScript loading');
// Enhanced filter function with genre support
function filterTracks() {
const sortFilter = document.getElementById('sort-filter').value;
const timeFilter = document.getElementById('time-filter').value;
const genreFilter = document.getElementById('genre-filter').value;
const url = new URL(window.location);
url.searchParams.set('sort', sortFilter);
url.searchParams.set('time', timeFilter);
if (genreFilter) {
url.searchParams.set('genre', genreFilter);
} else {
url.searchParams.delete('genre');
}
// Use AJAX to reload content instead of full page redirect
const newParams = new URLSearchParams(url.search);
reloadLibraryContent(newParams);
}
function filterByGenre(genre) {
const currentUrl = new URL(window.location);
currentUrl.searchParams.set('genre', genre);
currentUrl.searchParams.delete('page'); // Reset to page 1 when filtering
// Use AJAX to reload content instead of full page redirect
const newParams = new URLSearchParams(currentUrl.search);
reloadLibraryContent(newParams);
}
// Working like function with API integration
function toggleLike(trackId, button) {
console.log('π΅ User ID from PHP:', <?= $user_id ? $user_id : 'null' ?>);
console.log('π΅ Is logged in:', <?= $user_id ? 'true' : 'false' ?>);
if (!<?= $user_id ? 'true' : 'false' ?>) {
showNotification('Please log in to like tracks', 'warning');
return;
}
console.log('π΅ Toggling like for track:', trackId);
// Add loading state
button.style.pointerEvents = 'none';
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
fetch('/api_social.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'like', track_id: trackId })
})
.then(response => {
console.log('π΅ Like API response status:', response.status);
return response.json();
})
.then(data => {
console.log('π΅ Like API response data:', data);
if (data.success) {
button.classList.toggle('liked');
const countSpan = button.querySelector('.social-count');
if (countSpan) {
const currentCount = parseInt(countSpan.textContent);
countSpan.textContent = button.classList.contains('liked') ? currentCount + 1 : currentCount - 1;
}
// Show success notification
const action = button.classList.contains('liked') ? 'liked' : 'unliked';
showNotification(`Track ${action}!`, 'success');
} else {
showNotification(data.message || 'Failed to like track', 'error');
}
})
.catch(error => {
console.warn('π΅ Like error:', error);
showNotification('Failed to like track. Please try again.', 'error');
})
.finally(() => {
// Restore button
button.style.pointerEvents = 'auto';
button.innerHTML = originalText;
});
}
// Working comments function with modal
function showComments(trackId) {
// Create and show comments modal
const modal = document.createElement('div');
modal.className = 'comments-modal';
modal.innerHTML = `
<div class="comments-overlay">
<div class="comments-container">
<div class="comments-header">
<h3>Comments</h3>
<button class="close-btn" onclick="closeComments()">Γ</button>
</div>
<div class="comments-list" id="comments-list-${trackId}">
<div class="loading">Loading comments...</div>
</div>
<div class="comment-form">
<textarea id="comment-text-${trackId}" placeholder="Write a comment..." maxlength="500"></textarea>
<button onclick="addComment(${trackId})" class="btn btn-primary">Post Comment</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
// Load comments
loadComments(trackId);
}
function closeComments() {
const modal = document.querySelector('.comments-modal');
if (modal) {
modal.remove();
}
}
function loadComments(trackId) {
console.log('π΅ Loading comments for track:', trackId);
fetch(`/api_social.php?action=get_comments&track_id=${trackId}`)
.then(response => {
console.log('π΅ Load comments API response status:', response.status);
return response.json();
})
.then(data => {
console.log('π΅ Load comments API response data:', data);
const commentsList = document.getElementById(`comments-list-${trackId}`);
if (data.success && data.comments && data.comments.length > 0) {
commentsList.innerHTML = data.comments.map(comment => `
<div class="comment-item">
<div class="comment-avatar">
${comment.profile_image ?
`<img src="${comment.profile_image}" alt="${comment.user_name}" onerror="this.parentElement.innerHTML='<div class=\'default-avatar-small\'>${comment.user_name.charAt(0)}</div>'">` :
`<div class="default-avatar-small">${comment.user_name.charAt(0)}</div>`
}
</div>
<div class="comment-content">
<div class="comment-header">
<span class="comment-author">${comment.user_name}</span>
<span class="comment-time">${comment.created_at}</span>
</div>
<div class="comment-text">${comment.comment}</div>
</div>
</div>
`).join('');
} else {
commentsList.innerHTML = '<div class="no-comments">No comments yet. Be the first to comment!</div>';
}
})
.catch(error => {
console.warn('π΅ Load comments error:', error);
document.getElementById(`comments-list-${trackId}`).innerHTML = '<div class="error">Failed to load comments</div>';
});
}
function addComment(trackId) {
if (!<?= $user_id ? 'true' : 'false' ?>) {
showNotification('Please log in to comment', 'warning');
return;
}
const textarea = document.getElementById(`comment-text-${trackId}`);
const comment = textarea.value.trim();
if (!comment) {
showNotification('Please enter a comment', 'warning');
return;
}
// Show loading state
const submitBtn = textarea.nextElementSibling;
const originalText = submitBtn.textContent;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Posting...';
submitBtn.disabled = true;
fetch('/api_social.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'comment',
track_id: trackId,
comment: comment
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
textarea.value = '';
loadComments(trackId); // Reload comments
showNotification('Comment posted!', 'success');
} else {
showNotification(data.message || 'Failed to post comment', 'error');
}
})
.catch(error => {
console.warn('π΅ Comment error:', error);
showNotification('Failed to post comment. Please try again.', 'error');
})
.finally(() => {
// Restore button
submitBtn.textContent = originalText;
submitBtn.disabled = false;
});
}
// Notification system
function showNotification(message, type = 'info') {
// Remove existing notifications
const existingNotifications = document.querySelectorAll('.notification');
existingNotifications.forEach(notification => notification.remove());
// Create notification element
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.innerHTML = `
<div class="notification-content">
<i class="notification-icon ${getNotificationIcon(type)}"></i>
<span class="notification-message">${message}</span>
<button class="notification-close" onclick="this.parentElement.parentElement.remove()">
<i class="fas fa-times"></i>
</button>
</div>
`;
// Add to page
document.body.appendChild(notification);
// Show animation
setTimeout(() => {
notification.classList.add('show');
}, 100);
// Auto remove after 5 seconds
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
if (notification.parentElement) {
notification.remove();
}
}, 300);
}, 5000);
}
function getNotificationIcon(type) {
switch (type) {
case 'success': return 'fas fa-check-circle';
case 'error': return 'fas fa-exclamation-circle';
case 'warning': return 'fas fa-exclamation-triangle';
default: return 'fas fa-info-circle';
}
}
// Enhanced global player initialization check
function waitForEnhancedPlayerCallback(callback, maxAttempts = 20) {
if (window.enhancedGlobalPlayer && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
callback();
return;
}
if (maxAttempts > 0) {
setTimeout(() => waitForEnhancedPlayerCallback(callback, maxAttempts - 1), 250);
} else {
console.warn('β οΈ Enhanced global player not available, attempting to initialize...');
showNotification('Audio player not available. Please refresh the page.', 'error');
}
}
// Enable play buttons when global player is ready
function enablePlayButtons() {
console.log('π΅ Enabling play buttons - global player ready');
// Enable all play buttons
document.querySelectorAll('.action-btn.play-btn, .play-track-btn').forEach(btn => {
btn.classList.add('ready');
btn.disabled = false;
// Remove loading state if present
const icon = btn.querySelector('i');
if (icon && icon.className.includes('fa-spinner')) {
icon.className = 'fas fa-play';
}
});
// Show ready notification
showNotification('π΅ Audio player ready!', 'success');
}
// Professional music feed functions
function playTrack(trackId, audioUrl, title, artist) {
console.log('π΅ Playing track:', { trackId, audioUrl, title, artist });
// Ensure global player is ready before attempting playback
if (!window.enhancedGlobalPlayer || typeof window.enhancedGlobalPlayer.playTrack !== 'function') {
console.warn('β Global player not available');
showNotification('Audio player not ready. Please refresh the page.', 'error');
return;
}
try {
// Call the global player's playTrack function
const success = window.enhancedGlobalPlayer.playTrack(audioUrl, title, artist);
if (success) {
// Update UI - Mark this track as currently playing
document.querySelectorAll('.track-card').forEach(card => {
card.classList.remove('currently-playing', 'playing');
});
// Reset all play buttons
document.querySelectorAll('.action-btn.play-btn, .play-track-btn, .action-btn-compact.primary').forEach(btn => {
btn.classList.remove('playing');
const icon = btn.querySelector('i');
if (icon) {
icon.className = 'fas fa-play';
}
});
const currentCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (currentCard) {
currentCard.classList.add('currently-playing', 'playing');
// Update compact play button
const compactPlayBtn = currentCard.querySelector('.action-btn.play-btn, .action-btn-compact.primary');
if (compactPlayBtn) {
compactPlayBtn.classList.add('playing');
const icon = compactPlayBtn.querySelector('i');
if (icon) icon.className = 'fas fa-pause';
}
// Update full play button if exists
const fullPlayBtn = currentCard.querySelector('.play-track-btn');
if (fullPlayBtn) {
fullPlayBtn.classList.add('playing');
fullPlayBtn.innerHTML = '<i class="fas fa-pause"></i> <span>Playing</span>';
}
}
// Record play analytics
recordTrackPlay(trackId);
// Show notification
showNotification('π΅ Now playing: ' + title, 'success');
console.log('β
Track playing successfully');
} else {
console.warn('β Global player returned false');
showNotification('Failed to start playback. Please try again.', 'error');
}
} catch (error) {
console.warn('β Error during playback:', error);
showNotification('Playback error: ' + error.message, 'error');
}
}
function playTrackFromWaveform(trackId, audioUrl, title, artist) {
playTrack(trackId, audioUrl, title, artist);
}
function addToCart(trackId, title, price, artistPlan = 'free') {
if (!<?= $user_id ? 'true' : 'false' ?>) {
showNotification('Please log in to add tracks to cart', 'warning');
return;
}
console.log('π Adding to cart:', { trackId, title, price, artistPlan });
// Add loading state to button
const button = event.target.closest('.btn-cart');
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Adding...';
button.disabled = true;
// Send to cart.php via POST with artist plan info
const formData = new FormData();
formData.append('track_id', trackId);
formData.append('action', 'add');
formData.append('artist_plan', artistPlan);
fetch('cart.php', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(responseText => {
console.log('π Raw cart response:', responseText);
let data;
try {
data = JSON.parse(responseText);
console.log('π Parsed cart response:', data);
} catch (e) {
console.warn('π Failed to parse JSON response:', e);
console.warn('π Raw response was:', responseText);
throw new Error('Invalid JSON response from cart');
}
if (!data.success) {
throw new Error(data.message || 'Failed to add to cart');
}
if (price == 0) {
// Free track - make it feel like a premium purchase experience!
showNotification(`π΅ "${title}" added to cart for FREE! π Ready to purchase and own!`, 'success');
} else {
// Paid track added to cart
const revenueInfo = (artistPlan === 'free') ? ' (Platform Revenue)' : '';
showNotification(`"${title}" added to cart! ($${price})${revenueInfo}`, 'success');
}
// Update cart UI if there's a cart counter
const cartCounter = document.querySelector('.cart-count, .cart-counter');
if (cartCounter && data.cart_count) {
cartCounter.textContent = data.cart_count;
}
// Log debug info if available
if (data.debug) {
if (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) {
console.log('π Debug info:', data.debug);
}
}
// Change button to "Added" state temporarily
button.innerHTML = '<i class="fas fa-check"></i> Added!';
button.classList.add('added');
setTimeout(() => {
button.innerHTML = originalText;
button.classList.remove('added');
button.disabled = false;
}, 2000);
})
.catch(error => {
console.warn('π Cart error:', error);
showNotification('Failed to add to cart: ' + error.message, 'error');
// Restore button
button.innerHTML = originalText;
button.disabled = false;
});
}
function toggleFollow(userId, button) {
if (!<?= $user_id ? 'true' : 'false' ?>) {
showNotification('Please log in to follow artists', 'warning');
return;
}
console.log('π€ Toggling follow for user:', userId);
// Add loading state
button.style.pointerEvents = 'none';
const originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
fetch('/api_social.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'follow', user_id: userId })
})
.then(response => {
console.log('π€ Follow API response status:', response.status);
return response.json();
})
.then(data => {
console.log('π€ Follow API response data:', data);
if (data.success) {
button.classList.toggle('following');
const isFollowing = button.classList.contains('following');
button.innerHTML = `<i class="fas fa-user-${isFollowing ? 'check' : 'plus'}"></i> ${isFollowing ? 'Following' : 'Follow'}`;
// Show success notification
const action = isFollowing ? 'followed' : 'unfollowed';
showNotification(`Artist ${action}!`, 'success');
} else {
showNotification(data.message || 'Failed to follow artist', 'error');
}
})
.catch(error => {
console.warn('π€ Follow error:', error);
showNotification('Failed to follow artist. Please try again.', 'error');
})
.finally(() => {
// Restore button
button.style.pointerEvents = 'auto';
if (!button.innerHTML.includes('Following') && !button.innerHTML.includes('Follow')) {
button.innerHTML = originalText;
}
});
}
// Track play count functionality
function recordTrackPlay(trackId) {
// Only record if not already recorded recently
const lastPlayed = sessionStorage.getItem(`played_${trackId}`);
const now = Date.now();
if (!lastPlayed || (now - parseInt(lastPlayed)) > 30000) { // 30 seconds minimum between plays
fetch('/api_social.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'play', track_id: trackId })
})
.then(response => response.json())
.then(data => {
if (data.success) {
sessionStorage.setItem(`played_${trackId}`, now.toString());
console.log('π΅ Play count recorded for track:', trackId);
}
})
.catch(error => {
console.warn('π΅ Play count error:', error);
});
}
}
// Play button functionality is now handled by inline onclick="togglePlayPause()" calls
// This prevents conflicts and ensures proper parameter passing
document.addEventListener('DOMContentLoaded', function() {
console.log('π΅ Community Fixed - Initialized (play buttons use inline handlers)');
});
// π VIRAL TRACK SHARING SYSTEM
function showShareModal(trackId, title, artist) {
const trackUrl = `https://soundstudiopro.com/track/${trackId}`;
const shareText = `π΅ Check out "${title}" by ${artist}`;
// Update share URL input
document.getElementById('shareUrl').value = trackUrl;
// Update social share links
const platforms = {
twitter: `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(trackUrl)}`,
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(trackUrl)}`,
whatsapp: `https://wa.me/?text=${encodeURIComponent(shareText + ' ' + trackUrl)}`,
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(trackUrl)}`,
discord: `https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=${encodeURIComponent(trackUrl)}&response_type=code&scope=webhook.incoming`
};
// Update platform buttons
document.querySelector('[data-platform="twitter"]').onclick = () => openShare(platforms.twitter, 'twitter', trackId);
document.querySelector('[data-platform="facebook"]').onclick = () => openShare(platforms.facebook, 'facebook', trackId);
document.querySelector('[data-platform="whatsapp"]').onclick = () => openShare(platforms.whatsapp, 'whatsapp', trackId);
document.querySelector('[data-platform="linkedin"]').onclick = () => openShare(platforms.linkedin, 'linkedin', trackId);
document.querySelector('[data-platform="discord"]').onclick = () => copyDiscordLink(trackUrl, trackId);
document.getElementById('shareModal').style.display = 'block';
document.body.style.overflow = 'hidden';
}
function openShare(url, platform, trackId) {
window.open(url, '_blank', 'width=600,height=400');
recordShare(trackId, platform);
closeShareModal();
}
function copyShareUrl() {
const shareUrl = document.getElementById('shareUrl');
shareUrl.select();
shareUrl.setSelectionRange(0, 99999);
navigator.clipboard.writeText(shareUrl.value);
const copyBtn = document.querySelector('.copy-btn');
const originalText = copyBtn.textContent;
copyBtn.textContent = 'Copied!';
copyBtn.classList.add('copied');
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.classList.remove('copied');
}, 2000);
// Record share
const trackId = shareUrl.value.split('/track/')[1];
recordShare(trackId, 'copy-link');
}
function copyDiscordLink(url, text, trackId) {
const discordText = `π΅ ${text}\\n${url}`;
navigator.clipboard.writeText(discordText).then(() => {
recordShare(trackId, 'discord');
showNotification('Discord message copied! Paste it in your server π', 'success');
});
}
function copyInstagramLink(url, text, trackId) {
navigator.clipboard.writeText(url).then(() => {
recordShare(trackId, 'instagram');
showNotification('Link copied! Add it to your Instagram story πΈ', 'success');
});
}
function recordShare(trackId, platform) {
fetch('/api_social.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'share',
track_id: trackId,
platform: platform
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log(`π Share recorded: ${platform} for track ${trackId}`);
// Update share count in UI
const shareButton = document.querySelector(`[onclick*="${trackId}"] .social-count`);
if (shareButton) {
const currentCount = parseInt(shareButton.textContent);
shareButton.textContent = currentCount + 1;
}
}
})
.catch(error => {
console.warn('π Share recording error:', error);
});
}
// Handle shared track highlighting from URL
function handleSharedTrack() {
const urlParams = new URLSearchParams(window.location.search);
const trackId = urlParams.get('track');
if (trackId) {
// Find the track card
const trackCard = document.querySelector(`[data-track-id="${trackId}"]`)?.closest('.track-card');
if (trackCard) {
// Scroll to the track
setTimeout(() => {
trackCard.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// Add highlight effect
trackCard.classList.add('highlighted');
// Optional: Auto-play the track
const playButton = trackCard.querySelector('.play-track-btn');
if (playButton) {
setTimeout(() => {
playButton.click();
}, 1000);
}
// Remove highlight after animation
setTimeout(() => {
trackCard.classList.remove('highlighted');
}, 3000);
}, 500);
showNotification('π΅ Shared track found! Playing now...', 'success');
} else {
showNotification('Track not found on this page', 'warning');
}
}
}
// Initialize shared track handling
document.addEventListener('DOMContentLoaded', function() {
handleSharedTrack();
});
// Close modal when clicking outside
document.addEventListener('click', function(e) {
if (e.target.classList.contains('share-modal')) {
closeShareModal();
}
});
// DJ Mixing Features
function highlightCompatibleTracks(currentBpm, currentKey) {
const tracks = document.querySelectorAll('.track-card');
tracks.forEach(track => {
const bpmElement = track.querySelector('.bpm-value');
const keyElement = track.querySelector('.key-value');
if (bpmElement && keyElement) {
const trackBpm = parseInt(bpmElement.textContent);
const trackKey = keyElement.textContent;
// BPM compatibility (within 6% for mixing)
const bpmDiff = Math.abs(trackBpm - currentBpm);
const bpmCompatible = bpmDiff <= (currentBpm * 0.06);
// Key compatibility (same key, relative major/minor, perfect 5th)
const keyCompatible = isKeyCompatible(currentKey, trackKey);
// Add visual indicators
if (bpmCompatible) {
bpmElement.parentElement.classList.add('bpm-compatible');
}
if (keyCompatible) {
keyElement.parentElement.classList.add('key-compatible');
}
if (bpmCompatible && keyCompatible) {
track.classList.add('perfect-mix-match');
}
}
});
}
function isKeyCompatible(key1, key2) {
// Simplified key compatibility - same key, relative major/minor
if (key1 === key2) return true;
// Basic major/minor relative matching
const keyMap = {
'C major': 'A minor',
'A minor': 'C major',
'G major': 'E minor',
'E minor': 'G major',
'D major': 'B minor',
'B minor': 'D major',
'A major': 'F# minor',
'F# minor': 'A major',
'E major': 'C# minor',
'C# minor': 'E major',
'F major': 'D minor',
'D minor': 'F major',
'Bb major': 'G minor',
'G minor': 'Bb major'
};
return keyMap[key1] === key2;
}
function calculateBpmRange(bpm) {
const range = Math.round(bpm * 0.06);
return {
min: bpm - range,
max: bpm + range,
half: Math.round(bpm / 2),
double: bpm * 2
};
}
// Enhanced track clicking for DJ mode
function selectTrackForDjMode(trackElement) {
// Clear previous selections
document.querySelectorAll('.track-card.dj-selected').forEach(card => {
card.classList.remove('dj-selected');
});
// Mark as selected
trackElement.classList.add('dj-selected');
// Get technical info
const bpmElement = trackElement.querySelector('.bpm-value');
const keyElement = trackElement.querySelector('.key-value');
if (bpmElement && keyElement) {
const bpm = parseInt(bpmElement.textContent);
const key = keyElement.textContent;
// Highlight compatible tracks
highlightCompatibleTracks(bpm, key);
// Show DJ info panel
showDjMixingPanel(bpm, key);
}
}
function showDjMixingPanel(bpm, key) {
const bpmRange = calculateBpmRange(bpm);
// Create or update DJ panel
let djPanel = document.getElementById('dj-mixing-panel');
if (!djPanel) {
djPanel = document.createElement('div');
djPanel.id = 'dj-mixing-panel';
djPanel.className = 'dj-mixing-panel';
document.body.appendChild(djPanel);
}
djPanel.innerHTML = `
<div class="dj-panel-header">
<h3>π§ DJ Mixing Info</h3>
<button onclick="closeDjPanel()" class="close-dj-panel">Γ</button>
</div>
<div class="dj-panel-content">
<div class="current-track-info">
<div class="dj-info-item">
<label>Current BPM:</label>
<span class="dj-bpm">${bpm}</span>
</div>
<div class="dj-info-item">
<label>Current Key:</label>
<span class="dj-key">${key}</span>
</div>
</div>
<div class="mixing-suggestions">
<h4>π― Mixing Compatibility</h4>
<div class="bpm-suggestions">
<p><strong>BPM Range:</strong> ${bpmRange.min} - ${bpmRange.max}</p>
<p><strong>Half Time:</strong> ${bpmRange.half} BPM</p>
<p><strong>Double Time:</strong> ${bpmRange.double} BPM</p>
</div>
<div class="key-suggestions">
<p><strong>Compatible Keys:</strong> Same key, relative major/minor</p>
</div>
</div>
</div>
`;
djPanel.style.display = 'block';
}
function closeDjPanel() {
const djPanel = document.getElementById('dj-mixing-panel');
if (djPanel) {
djPanel.style.display = 'none';
}
// Clear all highlighting
document.querySelectorAll('.track-card').forEach(card => {
card.classList.remove('dj-selected', 'perfect-mix-match');
});
document.querySelectorAll('.bpm-compatible').forEach(el => {
el.classList.remove('bpm-compatible');
});
document.querySelectorAll('.key-compatible').forEach(el => {
el.classList.remove('key-compatible');
});
}
// Add click handlers for DJ mode
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.dj-tech-grid').forEach(grid => {
grid.addEventListener('click', function(e) {
e.preventDefault();
const trackCard = this.closest('.track-card');
selectTrackForDjMode(trackCard);
});
});
});
// Premium Rating System
function rateTrack(trackId, rating, starElement) {
if (!<?= $user_id ? 'true' : 'false' ?>) {
showNotification('Please log in to rate tracks', 'warning');
return;
}
console.log('β Rating track:', trackId, 'with', rating, 'stars');
// Add loading state
const ratingContainer = starElement.closest('.star-rating');
ratingContainer.style.pointerEvents = 'none';
fetch('/api_social.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'rate',
track_id: trackId,
rating: rating
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update visual rating
const stars = ratingContainer.querySelectorAll('.star');
stars.forEach((star, index) => {
star.classList.remove('filled', 'user-rated');
if (index < data.average_rating) {
star.classList.add('filled');
}
if (index < rating) {
star.classList.add('user-rated');
}
});
// Update stats
const avgElement = ratingContainer.closest('.track-rating-section').querySelector('.avg-rating');
const countElement = ratingContainer.closest('.track-rating-section').querySelector('.rating-count');
if (avgElement) avgElement.textContent = data.average_rating.toFixed(1) + '/10';
if (countElement) countElement.textContent = `(${data.rating_count} ratings)`;
showNotification(`β Rated ${rating}/10 stars!`, 'success');
} else {
showNotification(data.message || 'Failed to rate track', 'error');
}
})
.catch(error => {
console.warn('β Rating error:', error);
showNotification('Failed to rate track. Please try again.', 'error');
})
.finally(() => {
ratingContainer.style.pointerEvents = 'auto';
});
}
// Enhanced Play/Pause Logic
// Track currently playing track ID for state management
let currentlyPlayingTrackId = null;
function togglePlayPause(button, trackId, audioUrl, title, artist) {
const isCurrentlyPlaying = currentlyPlayingTrackId === trackId;
const globalPlayer = window.enhancedGlobalPlayer;
if (isCurrentlyPlaying && globalPlayer && globalPlayer.isPlaying) {
// Pause current track
globalPlayer.pause();
button.innerHTML = '<i class="fas fa-play"></i><span>Play</span>';
button.classList.remove('playing');
// Remove playing states
document.querySelectorAll('.track-card').forEach(card => {
card.classList.remove('currently-playing');
});
currentlyPlayingTrackId = null;
showNotification('βΈοΈ Paused', 'success');
} else {
// Play new track or resume
if (globalPlayer && typeof globalPlayer.playTrack === 'function') {
globalPlayer.playTrack(audioUrl, title, artist);
// Update all play buttons
document.querySelectorAll('.play-track-btn').forEach(btn => {
btn.classList.remove('playing');
btn.innerHTML = '<i class="fas fa-play"></i><span>Play</span>';
});
// Update current button
button.classList.add('playing');
button.innerHTML = '<i class="fas fa-pause"></i><span>Playing</span>';
// Update track cards
document.querySelectorAll('.track-card').forEach(card => {
card.classList.remove('currently-playing');
});
const currentCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (currentCard) {
currentCard.classList.add('currently-playing');
}
currentlyPlayingTrackId = trackId;
recordTrackPlay(trackId);
showNotification('π΅ Now playing: ' + title, 'success');
} else {
showNotification('Player not ready, please try again', 'error');
}
}
}
// View Track Charts Function
function viewTrackCharts(trackId, genre) {
// Build chart URL with track context
const chartUrl = `/charts.php?track=${trackId}&genre=${encodeURIComponent(genre)}&highlight=true`;
// Open charts in new tab or redirect
if (confirm('View this track in the global charts?')) {
window.open(chartUrl, '_blank');
}
}
// Charts Modal Functions
function showChartsModal(trackId, position, title, genre) {
document.getElementById('currentPosition').textContent = '#' + position;
document.getElementById('trackTitle').textContent = title;
document.getElementById('genreRanking').textContent = '#' + Math.floor(Math.random() * 20 + 1) + ' in ' + genre;
document.getElementById('weeklyPlays').textContent = (Math.floor(Math.random() * 1000) + 100).toLocaleString();
document.getElementById('totalRating').textContent = (Math.random() * 3 + 7).toFixed(1) + '/10';
// Random position change
const change = Math.floor(Math.random() * 20) - 10;
const changeElement = document.getElementById('positionChange');
if (change > 0) {
changeElement.textContent = 'β +' + change + ' this week';
changeElement.className = 'position-change';
} else if (change < 0) {
changeElement.textContent = 'β ' + change + ' this week';
changeElement.className = 'position-change down';
} else {
changeElement.textContent = 'β No change';
changeElement.className = 'position-change';
}
document.getElementById('chartsModal').style.display = 'flex';
document.body.style.overflow = 'hidden';
}
function closeChartsModal() {
document.getElementById('chartsModal').style.display = 'none';
document.body.style.overflow = 'auto';
}
function openFullCharts() {
closeChartsModal();
window.open('/charts.php', '_blank');
}
// Premium Modal Functions
function showPremiumModal() {
document.getElementById('premiumModal').style.display = 'flex';
document.body.style.overflow = 'hidden';
}
function closePremiumModal() {
document.getElementById('premiumModal').style.display = 'none';
document.body.style.overflow = 'auto';
}
function upgradeToPremium() {
closePremiumModal();
window.location.href = '/upgrade.php';
}
// Close modals when clicking outside
window.onclick = function(event) {
const chartsModal = document.getElementById('chartsModal');
const premiumModal = document.getElementById('premiumModal');
if (event.target === chartsModal) {
closeChartsModal();
}
if (event.target === premiumModal) {
closePremiumModal();
}
}
// Toggle Lyrics Function
function toggleLyrics(trackId) {
const lyricsContent = document.getElementById('lyrics-' + trackId);
const toggleButton = lyricsContent.previousElementSibling;
const toggleSpan = toggleButton.querySelector('span');
const toggleIcon = toggleButton.querySelector('.toggle-icon');
if (lyricsContent.style.display === 'none') {
lyricsContent.style.display = 'block';
lyricsContent.classList.add('expanded');
toggleButton.classList.add('active');
toggleSpan.textContent = 'Hide Lyrics';
} else {
lyricsContent.classList.remove('expanded');
toggleButton.classList.remove('active');
toggleSpan.textContent = 'Show Lyrics';
setTimeout(() => {
lyricsContent.style.display = 'none';
}, 400);
}
}
// Add to Playlist Function
function addToPlaylist(trackId) {
// TODO: Implement playlist functionality
showNotification('π΅ Playlist feature coming soon!', 'info');
}
// Add to Cart Function
function addToCart(trackId, title, price, button) {
button.disabled = true;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
fetch('/cart.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `action=add&track_id=${trackId}&title=${encodeURIComponent(title)}&price=${price}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification(`π "${title}" added to cart!`, 'success');
// Update cart counter if it exists
const cartCounter = document.querySelector('.cart-counter');
if (cartCounter) {
cartCounter.textContent = data.cart_count || '';
cartCounter.style.display = data.cart_count > 0 ? 'block' : 'none';
}
} else {
showNotification(data.message || 'Failed to add to cart', 'error');
}
})
.catch(error => {
console.warn('Cart error:', error);
showNotification('Network error', 'error');
})
.finally(() => {
button.disabled = false;
button.innerHTML = '<i class="fas fa-shopping-cart"></i>';
});
}
// Record Track Play
function recordTrackPlay(trackId) {
fetch('/api_social.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `action=play&track_id=${trackId}`
})
.catch(error => console.warn('Play tracking error:', error));
}
// Toggle Follow Function
function toggleFollow(userId, button) {
const isFollowing = button.classList.contains('following');
const action = isFollowing ? 'unfollow' : 'follow';
fetch('/api_social.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `action=${action}&user_id=${userId}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
button.classList.toggle('following');
const icon = button.querySelector('i');
const text = button.querySelector('span') || button.childNodes[1];
if (isFollowing) {
icon.className = 'fas fa-user-plus';
if (text) text.textContent = ' Follow';
showNotification('Unfollowed artist', 'info');
} else {
icon.className = 'fas fa-user-check';
if (text) text.textContent = ' Following';
showNotification('Following artist!', 'success');
}
} else {
showNotification('Failed to update follow status', 'error');
}
})
.catch(error => {
console.warn('Follow error:', error);
showNotification('Network error', 'error');
});
}
</script>
<script>
// Initialize enhanced global player integration
document.addEventListener('DOMContentLoaded', function() {
console.log('π΅ Community page loaded, waiting for enhanced global player...');
// Wait for enhanced global player to be ready, then enable play buttons
waitForEnhancedPlayerCallback(() => {
enablePlayButtons();
});
});
</script>
<!-- Include the existing modals and JavaScript functions -->
<!-- This ensures all functionality is preserved -->
<!-- Variations Modal -->
<div id="variationsModal" class="variations-modal">
<div class="variations-content">
<div class="variations-header">
<h2 class="variations-title">Choose Your Track Variation</h2>
<button class="close-variations" onclick="closeVariations()">
<i class="fas fa-times"></i>
</button>
</div>
<div id="variationsGrid" class="variations-grid">
<!-- Variations will be loaded here -->
</div>
<div class="variations-footer">
<div class="variations-info">
<i class="fas fa-info-circle"></i>
Select which variation will be your main track for sale and public display. You can also download individual variations or all versions at once.
</div>
<div class="variations-actions">
<button class="variations-btn cancel" onclick="closeVariations()">
Cancel
</button>
<button id="saveVariationBtn" class="variations-btn save" onclick="saveVariationSelection()" disabled>
Save Selection
</button>
</div>
</div>
</div>
</div>
<!-- Lyrics Modal -->
<div id="lyricsModal" class="lyrics-modal">
<div class="lyrics-content">
<div class="lyrics-header">
<h2 class="lyrics-title">Track Lyrics</h2>
<button class="close-lyrics" onclick="closeLyrics()">
<i class="fas fa-times"></i>
</button>
</div>
<div id="lyricsContent" class="lyrics-body">
<!-- Lyrics will be loaded here -->
</div>
<div class="lyrics-footer">
<div class="lyrics-actions">
<button class="lyrics-btn cancel" onclick="closeLyrics()">
Close
</button>
<button class="lyrics-btn copy" onclick="copyLyrics()">
<i class="fas fa-copy"></i> Copy Lyrics
</button>
</div>
</div>
</div>
</div>
<!-- Download Modal -->
<div id="downloadModal" class="download-modal">
<div class="download-content">
<div class="download-header">
<h2 class="download-title">Download Track</h2>
<button class="close-download" onclick="closeDownload()">
<i class="fas fa-times"></i>
</button>
</div>
<div id="downloadContent" class="download-body">
<!-- Download options will be loaded here -->
</div>
<div class="download-footer">
<div class="download-actions">
<button class="download-btn cancel" onclick="closeDownload()">
Cancel
</button>
</div>
</div>
</div>
</div>
<script>
// Global variables for variations functionality
window.trackVariations = [];
window.currentTrackId = null;
window.selectedVariationIndex = null;
// Show variations modal
function showVariations(trackId) {
console.log('π΅ Showing variations for track:', trackId);
// Get track data from PHP
const trackCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (!trackCard) {
console.warn('π΅ Track card not found for track ID:', trackId);
alert('Track not found. Please refresh the page and try again.');
return;
}
console.log('π΅ Track card found:', trackCard);
// Get variations data from PHP
const variationsData = trackCard.getAttribute('data-variations');
console.log('π΅ Raw variations data:', variationsData);
// Debug: Check if variations data exists
if (!variationsData) {
console.warn('π΅ No variations data attribute found');
alert('No variations data found for this track.');
return;
}
if (variationsData === '[]' || variationsData === 'null') {
console.warn('π΅ Variations data is empty');
alert('No variations available for this track.');
return;
}
try {
if (typeof window.trackVariations === 'undefined') {
window.trackVariations = [];
}
window.trackVariations = JSON.parse(variationsData);
console.log('π΅ Parsed variations:', window.trackVariations);
if (!Array.isArray(window.trackVariations) || window.trackVariations.length === 0) {
console.warn('π΅ No variations in array');
alert('No variations available for this track.');
return;
}
window.currentTrackId = trackId;
// Get current selection
const currentSelection = trackCard.getAttribute('data-selected-variation') || '0';
window.selectedVariationIndex = parseInt(currentSelection);
console.log('π΅ About to populate variations grid');
// Populate variations grid
populateVariationsGrid();
console.log('π΅ About to show modal');
// Show modal
const modal = document.getElementById('variationsModal');
if (!modal) {
console.warn('π΅ Variations modal not found');
alert('Modal not found. Please refresh the page and try again.');
return;
}
modal.classList.add('active');
console.log('π΅ Modal should now be visible');
// Force modal to be visible and on top
modal.style.zIndex = '999999';
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100%';
modal.style.height = '100%';
modal.style.display = 'flex';
modal.style.alignItems = 'center';
modal.style.justifyContent = 'center';
console.log('π΅ Modal styles applied:', {
zIndex: modal.style.zIndex,
position: modal.style.position,
display: modal.style.display
});
// Close on outside click
modal.addEventListener('click', function(e) {
if (e.target === this) {
closeVariations();
}
});
} catch (error) {
console.warn('π΅ Error parsing variations data:', error);
alert('Error loading variations. Please refresh the page and try again.');
}
}
// Populate variations grid
function populateVariationsGrid() {
const grid = document.getElementById('variationsGrid');
grid.innerHTML = '';
window.trackVariations.forEach((variation, index) => {
const card = document.createElement('div');
card.className = `variation-card ${index === window.selectedVariationIndex ? 'selected' : ''}`;
card.onclick = () => selectVariation(index);
const duration = Math.floor(variation.duration / 60) + 'm ' + Math.floor(variation.duration % 60) + 's';
const tags = variation.tags ? variation.tags.split(',').slice(0, 3) : [];
card.innerHTML = `
<div class="variation-header">
<div class="variation-title">${variation.title || 'Variation ' + (index + 1)}</div>
<div class="variation-index">${index + 1}</div>
</div>
<div class="variation-duration">
<i class="fas fa-clock"></i> ${duration}
</div>
${tags.length > 0 ? `
<div class="variation-tags">
${tags.map(tag => `<span class="variation-tag">${tag.trim()}</span>`).join('')}
</div>
` : ''}
<div class="variation-actions">
<button class="variation-btn play" onclick="playVariation(${index})">
<i class="fas fa-play"></i> Play
</button>
<button class="variation-btn download" onclick="downloadVariation(${index})">
<i class="fas fa-download"></i> Download
</button>
<button class="variation-btn select ${index === window.selectedVariationIndex ? 'selected' : ''}" onclick="selectVariation(${index})">
<i class="fas fa-check"></i> ${index === window.selectedVariationIndex ? 'Selected' : 'Select'}
</button>
</div>
`;
grid.appendChild(card);
});
// Update save button state
updateSaveButton();
}
// Select variation
function selectVariation(index) {
window.selectedVariationIndex = index;
// Update visual selection
document.querySelectorAll('.variation-card').forEach((card, i) => {
card.classList.toggle('selected', i === index);
});
document.querySelectorAll('.variation-btn.select').forEach((btn, i) => {
btn.classList.toggle('selected', i === index);
btn.innerHTML = `<i class="fas fa-check"></i> ${i === index ? 'Selected' : 'Select'}`;
});
updateSaveButton();
}
// Play variation
async function playVariation(index) {
const variation = window.trackVariations[index];
if (!variation) return;
console.log('π΅ Playing variation:', variation);
// Wait for enhanced global player to be ready
await waitForEnhancedPlayer();
// Use the enhanced global player (same as community.php)
if (typeof window.enhancedGlobalPlayer !== 'undefined' && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
console.log('π΅ Using enhanced global player for variation');
window.enhancedGlobalPlayer.playTrack(variation.audio_url, variation.title || 'Variation ' + (index + 1), '<?= htmlspecialchars($user_name) ?>');
} else {
console.warn('π΅ Enhanced global player not available for variation playback');
alert('Audio player not available. Please refresh the page and try again.');
}
}
// Download variation
function downloadVariation(index) {
const variation = window.trackVariations[index];
if (!variation) return;
console.log('π΅ Downloading variation:', variation);
// Create a temporary link element to trigger download
const link = document.createElement('a');
link.href = variation.audio_url;
// Create a meaningful filename
const trackTitle = variation.title || 'Variation ' + (index + 1);
const artistName = '<?= htmlspecialchars($user_name) ?>';
const filename = `${artistName} - ${trackTitle} (Variation ${index + 1}).mp3`;
link.download = filename;
link.target = '_blank';
// Add to DOM, click, and remove
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Show success message
const toast = document.createElement('div');
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(72, 187, 120, 0.9);
color: white;
padding: 1rem 1.5rem;
border-radius: 8px;
font-weight: 600;
z-index: 10000;
backdrop-filter: blur(10px);
`;
toast.textContent = 'β
Variation downloaded successfully!';
document.body.appendChild(toast);
setTimeout(() => {
document.body.removeChild(toast);
}, 3000);
}
// Update save button state
function updateSaveButton() {
const saveBtn = document.getElementById('saveVariationBtn');
if (!saveBtn) {
console.warn('π΅ Save button not found');
return;
}
if (window.currentTrackId === null) {
saveBtn.disabled = true;
saveBtn.textContent = 'No Track Selected';
return;
}
const trackCard = document.querySelector(`[data-track-id="${window.currentTrackId}"]`);
if (!trackCard) {
saveBtn.disabled = true;
saveBtn.textContent = 'Track Not Found';
return;
}
const currentSelection = trackCard.getAttribute('data-selected-variation') || '0';
if (window.selectedVariationIndex !== parseInt(currentSelection)) {
saveBtn.disabled = false;
saveBtn.textContent = 'Save Selection';
} else {
saveBtn.disabled = true;
saveBtn.textContent = 'No Changes';
}
}
// Save variation selection
function saveVariationSelection() {
if (window.selectedVariationIndex === null || window.currentTrackId === null) {
console.warn('π΅ No variation or track selected');
return;
}
console.log('π΅ Saving variation selection:', { trackId: window.currentTrackId, variationIndex: window.selectedVariationIndex });
// Disable save button during request
const saveBtn = document.getElementById('saveVariationBtn');
saveBtn.disabled = true;
saveBtn.textContent = 'Saving...';
fetch('/api_select_variation.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
track_id: window.currentTrackId,
variation_index: window.selectedVariationIndex
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('π΅ Variation selection saved:', data);
// Update the track card
const trackCard = document.querySelector(`[data-track-id="${window.currentTrackId}"]`);
if (trackCard) {
trackCard.setAttribute('data-selected-variation', window.selectedVariationIndex);
// Update the main audio URL and duration
const variation = window.trackVariations[window.selectedVariationIndex];
if (variation) {
// Update play button
const playBtn = trackCard.querySelector('.play-track-btn');
if (playBtn) {
playBtn.setAttribute('data-audio-url', variation.audio_url);
}
// Update duration display
const durationSpan = trackCard.querySelector('.track-details span:first-child');
if (durationSpan) {
const duration = Math.floor(variation.duration / 60) + 'm ' + Math.floor(variation.duration % 60) + 's';
durationSpan.innerHTML = `<i class="fas fa-clock"></i> ${duration}`;
}
}
}
// Show success message
if (typeof window.showNotification === 'function') {
window.showNotification('Variation selection saved successfully!', 'success');
} else {
alert('β
Variation selection saved successfully!');
}
// Close modal
closeVariations();
} else {
console.warn('π΅ Failed to save variation selection:', data.error);
if (typeof window.showNotification === 'function') {
window.showNotification('Failed to save variation selection: ' + (data.error || 'Unknown error'), 'error');
} else {
alert('β Failed to save variation selection: ' + (data.error || 'Unknown error'));
}
}
})
.catch(error => {
console.warn('π΅ Error saving variation selection:', error);
if (typeof window.showNotification === 'function') {
window.showNotification('Network error saving selection. Please check your connection and try again.', 'error');
} else {
alert('β Network error saving selection. Please check your connection and try again.');
}
})
.finally(() => {
// Re-enable save button
saveBtn.disabled = false;
updateSaveButton();
});
}
// Close variations modal
function closeVariations() {
const modal = document.getElementById('variationsModal');
modal.classList.remove('active');
// Reset variables
window.trackVariations = [];
window.currentTrackId = null;
window.selectedVariationIndex = null;
}
// Show lyrics modal
function showLyrics(trackId) {
console.log('π΅ Showing lyrics for track:', trackId);
// Get track data
const trackCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (!trackCard) {
console.warn('π΅ Track card not found for track ID:', trackId);
alert('Track not found. Please refresh the page and try again.');
return;
}
console.log('π΅ Track card found:', trackCard);
// Get track title
const titleElement = trackCard.querySelector('.track-title-mini');
const trackTitle = titleElement ? titleElement.textContent : 'Unknown Track';
console.log('π΅ Track title:', trackTitle);
// Show lyrics modal
const modal = document.getElementById('lyricsModal');
const lyricsContent = document.getElementById('lyricsContent');
console.log('π΅ Modal element:', modal);
console.log('π΅ Lyrics content element:', lyricsContent);
if (!modal || !lyricsContent) {
console.warn('π΅ Modal elements not found');
alert('Modal not found. Please refresh the page and try again.');
return;
}
// For now, show placeholder lyrics since lyrics functionality needs to be implemented
lyricsContent.innerHTML = `
<div style="text-align: center; padding: 2rem;">
<i class="fas fa-music" style="font-size: 3rem; color: #667eea; margin-bottom: 1rem;"></i>
<h3 style="color: #ffffff; margin-bottom: 1rem;">${trackTitle}</h3>
<p style="color: #a0aec0; margin-bottom: 2rem;">π΅ Lyrics feature coming soon!</p>
<div style="background: rgba(102, 126, 234, 0.1); padding: 1.5rem; border-radius: 8px; border: 1px solid rgba(102, 126, 234, 0.3);">
<p style="color: #cccccc; font-style: italic;">
"This will show the AI-generated lyrics for your track.<br>
Stay tuned for this exciting feature!"
</p>
</div>
</div>
`;
// Show modal
modal.classList.add('active');
console.log('π΅ Lyrics modal activated');
// Force modal to be visible and on top
modal.style.zIndex = '999999';
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100%';
modal.style.height = '100%';
modal.style.display = 'flex';
modal.style.alignItems = 'center';
modal.style.justifyContent = 'center';
console.log('π΅ Lyrics modal styles applied:', {
zIndex: modal.style.zIndex,
position: modal.style.position,
display: modal.style.display
});
// Close on outside click
modal.addEventListener('click', function(e) {
if (e.target === this) {
closeLyrics();
}
});
}
// Close lyrics modal
function closeLyrics() {
const modal = document.getElementById('lyricsModal');
modal.classList.remove('active');
}
// Copy lyrics to clipboard
function copyLyrics() {
const lyricsContent = document.getElementById('lyricsContent');
if (lyricsContent) {
const text = lyricsContent.textContent || lyricsContent.innerText;
navigator.clipboard.writeText(text).then(() => {
alert('β
Lyrics copied to clipboard!');
}).catch(() => {
alert('β Failed to copy lyrics. Please select and copy manually.');
});
}
}
// Download track
function downloadTrack(trackId) {
console.log('π΅ Downloading track:', trackId);
// Get track data
const trackCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (!trackCard) {
console.warn('π΅ Track card not found for track ID:', trackId);
alert('Track not found. Please refresh the page and try again.');
return;
}
// Show download modal
const modal = document.getElementById('downloadModal');
const downloadContent = document.getElementById('downloadContent');
if (!modal || !downloadContent) {
console.warn('π΅ Download modal elements not found');
alert('Download modal not found. Please refresh the page and try again.');
return;
}
// Get track title
const titleElement = trackCard.querySelector('.track-title-mini');
const trackTitle = titleElement ? titleElement.textContent : 'Unknown Track';
// Populate download modal
downloadContent.innerHTML = `
<div style="text-align: center; padding: 2rem;">
<i class="fas fa-download" style="font-size: 3rem; color: #667eea; margin-bottom: 1rem;"></i>
<h3 style="color: #ffffff; margin-bottom: 1rem;">${trackTitle}</h3>
<p style="color: #a0aec0; margin-bottom: 2rem;">Choose your download option:</p>
<div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
<button onclick="downloadSingleTrack(${trackId})" style="background: linear-gradient(135deg, #667eea, #764ba2); color: white; border: none; padding: 1rem 2rem; border-radius: 8px; cursor: pointer; margin: 0.5rem;">
<i class="fas fa-music"></i> Download Track
</button>
<button onclick="downloadAllVariations(${trackId})" style="background: linear-gradient(135deg, #48bb78, #38a169); color: white; border: none; padding: 1rem 2rem; border-radius: 8px; cursor: pointer; margin: 0.5rem;">
<i class="fas fa-layer-group"></i> Download All Variations
</button>
</div>
</div>
`;
// Show modal
modal.classList.add('active');
console.log('π΅ Download modal activated');
// Add click outside to close
modal.addEventListener('click', function(e) {
if (e.target === this) {
closeDownload();
}
});
// Force modal to be visible and on top
modal.style.zIndex = '999999';
modal.style.position = 'fixed';
modal.style.top = '0';
modal.style.left = '0';
modal.style.width = '100%';
modal.style.height = '100%';
modal.style.display = 'flex';
modal.style.alignItems = 'center';
modal.style.justifyContent = 'center';
}
// Close download modal
function closeDownload() {
const modal = document.getElementById('downloadModal');
if (modal) {
modal.classList.remove('active');
// Reset any forced styles
modal.style.zIndex = '';
modal.style.position = '';
modal.style.top = '';
modal.style.left = '';
modal.style.width = '';
modal.style.height = '';
modal.style.display = '';
modal.style.alignItems = '';
modal.style.justifyContent = '';
}
}
// Download single track
function downloadSingleTrack(trackId) {
const trackCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (!trackCard) return;
// Get the play button which has the audio data
const playBtn = trackCard.querySelector('.action-btn-compact.primary');
if (!playBtn) {
showNotification('β Play button not found. Please try again.', 'error');
return;
}
const audioUrl = playBtn.getAttribute('data-audio-url');
const title = playBtn.getAttribute('data-title');
const artist = playBtn.getAttribute('data-artist');
if (!audioUrl) {
showNotification('β Audio URL not found. Please try again.', 'error');
return;
}
// Create download link
const link = document.createElement('a');
link.href = audioUrl;
link.download = `${artist} - ${title}.mp3`;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Show success message
showNotification('β
Track downloaded successfully!', 'success');
closeDownload();
}
// Download all variations
function downloadAllVariations(trackId) {
const trackCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (!trackCard) return;
const variationsData = trackCard.getAttribute('data-variations');
if (!variationsData || variationsData === '[]') {
showNotification('β No variations available for this track.', 'warning');
return;
}
try {
const variations = JSON.parse(variationsData);
if (!Array.isArray(variations) || variations.length === 0) {
showNotification('β No variations available for this track.', 'warning');
return;
}
// Download each variation
variations.forEach((variation, index) => {
setTimeout(() => {
const link = document.createElement('a');
link.href = variation.audio_url;
link.download = `Variation ${index + 1}.mp3`;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}, index * 500); // Stagger downloads
});
showNotification('β
All variations download started!', 'success');
closeDownload();
} catch (error) {
console.warn('Error downloading variations:', error);
showNotification('β Error downloading variations. Please try again.', 'error');
}
}
// Genre expansion toggle function
function toggleGenreExpansion(button, hiddenGenres) {
const trackCard = button.closest('.track-card');
const expandedGenres = trackCard.querySelector('.expanded-genres');
if (expandedGenres.style.display === 'none') {
// Show expanded genres
expandedGenres.style.display = 'flex';
button.textContent = 'Show less';
button.classList.add('expanded');
} else {
// Hide expanded genres
expandedGenres.style.display = 'none';
button.textContent = `+${hiddenGenres.length} more`;
button.classList.remove('expanded');
}
}
// Update currently playing track
function updateCurrentlyPlaying(trackId) {
// Remove currently playing class from all tracks
document.querySelectorAll('.track-card').forEach(card => {
card.classList.remove('currently-playing', 'playing');
});
// Add currently playing class to the current track
if (trackId) {
const currentCard = document.querySelector(`[data-track-id="${trackId}"]`);
if (currentCard) {
currentCard.classList.add('currently-playing', 'playing');
}
}
}
// Enhanced play track function with playing state
async function playTrackFromButton(audioUrl, title, artist) {
console.log('π΅ Library: playTrackFromButton called with:', { audioUrl, title, artist });
if (!audioUrl) {
console.warn('No audio URL provided');
return;
}
// Wait for enhanced global player to be ready
await waitForEnhancedPlayer();
// Use the enhanced global player (same as community.php)
if (typeof window.enhancedGlobalPlayer !== 'undefined' && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
window.enhancedGlobalPlayer.playTrack(audioUrl, title, artist);
// Update currently playing state
const trackId = getTrackIdFromAudioUrl(audioUrl);
console.log('π΅ Library: Track ID for playing state:', trackId);
updateCurrentlyPlaying(trackId);
console.log('π΅ Library: Track sent via enhancedGlobalPlayer -', title);
} else {
console.error('Enhanced global player not available');
alert('Audio player not available. Please refresh the page and try again.');
}
}
// Get track ID from audio URL
function getTrackIdFromAudioUrl(audioUrl) {
// Find the button with this audio URL and get its track ID directly
const button = document.querySelector(`[data-audio-url="${audioUrl}"]`);
if (button) {
return button.getAttribute('data-track-id');
}
return null;
}
// Toggle generation parameters visibility
function toggleGenerationParams(header) {
const paramsGrid = header.nextElementSibling;
const toggleIcon = header.querySelector('.toggle-icon');
if (paramsGrid.style.display === 'none') {
paramsGrid.style.display = 'grid';
toggleIcon.classList.remove('fa-chevron-down');
toggleIcon.classList.add('fa-chevron-up');
} else {
paramsGrid.style.display = 'none';
toggleIcon.classList.remove('fa-chevron-up');
toggleIcon.classList.add('fa-chevron-down');
}
}
// Play track from waveform (like community_fixed.php)
function playTrackFromWaveform(trackId, audioUrl, title, artist) {
console.log('π΅ Library: playTrackFromWaveform called with:', { trackId, audioUrl, title, artist });
playTrackFromButton(audioUrl, title, artist);
}
// Wait for enhanced global player to be ready
function waitForEnhancedPlayer() {
return new Promise((resolve) => {
if (window.enhancedGlobalPlayer) {
resolve();
} else {
const checkPlayer = () => {
if (window.enhancedGlobalPlayer) {
resolve();
} else {
setTimeout(checkPlayer, 100);
}
};
checkPlayer();
}
});
}
// Other library functions
async function playTrackFromButtonElement(button) {
const audioUrl = button.getAttribute('data-audio-url');
const title = button.getAttribute('data-title');
const artist = button.getAttribute('data-artist');
if (!audioUrl) {
showNotification('β Audio URL not found. Please try again.', 'error');
return;
}
// Wait for enhanced global player to be ready
await waitForEnhancedPlayer();
// Use the enhanced global player (same as community.php)
if (typeof window.enhancedGlobalPlayer !== 'undefined' && typeof window.enhancedGlobalPlayer.playTrack === 'function') {
window.enhancedGlobalPlayer.playTrack(audioUrl, title, artist);
console.log('π΅ Library: Track sent via enhancedGlobalPlayer -', title);
} else {
console.warn('Enhanced global player not available');
alert('Audio player not available. Please refresh the page and try again.');
}
}
function checkTrackStatus(trackId) {
console.log('Checking status for track:', trackId);
// Implementation for checking track status
showNotification('Status check feature coming soon!', 'info');
}
function retryTrack(trackId) {
console.log('Retrying track:', trackId);
// Implementation for retrying failed tracks
showNotification('Retry feature coming soon!', 'info');
}
function showFailureHelp(trackId) {
console.log('Showing failure help for track:', trackId);
// Implementation for showing failure help
showNotification('Failure help feature coming soon!', 'info');
}
function deleteFailedTrack(trackId) {
if (confirm('Are you sure you want to delete this failed track?')) {
console.log('Deleting failed track:', trackId);
// Implementation for deleting failed tracks
showNotification('Delete feature coming soon!', 'info');
}
}
// Initialize enhanced global player integration
document.addEventListener('DOMContentLoaded', function() {
console.log('π΅ Library page loaded, initializing enhanced global player...');
// Wait for enhanced global player to be ready
waitForEnhancedPlayer().then(() => {
console.log('π΅ Enhanced global player ready for library page');
// Test enhanced global player functions
console.log('π΅ Available enhanced global player functions:', {
enhancedGlobalPlayer: typeof window.enhancedGlobalPlayer,
playTrack: typeof window.enhancedGlobalPlayer?.playTrack
});
}).catch(error => {
console.warn('π΅ Enhanced global player initialization error:', error);
});
// Enhanced filter handling
const filterSelects = document.querySelectorAll('select[name="sort"], select[name="time"], select[name="genre"], select[name="status"]');
filterSelects.forEach(select => {
select.addEventListener('change', function() {
// Build URL with current filters
const url = new URL(window.location);
const params = new URLSearchParams(url.search);
// Update the changed parameter
params.set(this.name, this.value);
// Reset to page 1 when filters change
params.delete('page');
// Navigate to new URL
// Use AJAX to reload content instead of full page redirect
reloadLibraryContent(params);
});
});
// Close modal when clicking outside
const modal = document.getElementById('variationsModal');
if (modal) {
modal.addEventListener('click', function(e) {
if (e.target === this) {
closeVariations();
}
});
}
// All tracks loaded at once - no pagination needed
console.log('π΅ All tracks loaded - infinite scroll not needed');
});
</script>
<!-- Track Edit Modal -->
<div id="editTrackModal" class="modal">
<div class="modal-content">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<h2>Edit Track</h2>
<button onclick="closeEditModal()" class="btn">
<i class="fas fa-times"></i>
</button>
</div>
<form method="POST" id="editTrackForm">
<input type="hidden" name="action" value="update_track">
<input type="hidden" name="track_id" id="editTrackId">
<div class="form-group">
<label class="form-label">Track Title</label>
<input type="text" name="title" id="editTitle" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label">Description/Prompt</label>
<textarea name="description" id="editDescription" class="form-input" rows="3" placeholder="Describe your track..."></textarea>
</div>
<div class="form-group">
<label class="form-label">Price (USD)</label>
<input type="number" name="price" id="editPrice" class="form-input" min="0" step="0.01" placeholder="0.00">
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox" name="is_public" id="editIsPublic">
Make this track public
</label>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Update Track</button>
<button type="button" onclick="closeEditModal()" class="btn">Cancel</button>
</div>
</form>
</div>
</div>
<script>
function editTrack(trackId, title, description, price, isPublic) {
document.getElementById('editTrackId').value = trackId;
document.getElementById('editTitle').value = title;
document.getElementById('editDescription').value = description || '';
document.getElementById('editPrice').value = price || '0';
document.getElementById('editIsPublic').checked = isPublic == 1;
document.getElementById('editTrackModal').style.display = 'block';
}
function closeEditModal() {
document.getElementById('editTrackModal').style.display = 'none';
}
// Close modal when clicking outside
window.onclick = function(event) {
const modal = document.getElementById('editTrackModal');
if (event.target === modal) {
closeEditModal();
}
}
</script>
<?php include 'includes/footer.php'; ?>