update design for download missing track modal

pull/49/head
Broque Thomas 8 months ago
parent ba85c3aaa7
commit c3dd91d676

@ -0,0 +1,39 @@
{
"bubbles": {
"7oPftvlwr6VrsViSDV7fJY": {
"artist": {
"confidence": 1,
"genres": [
"punk",
"pop punk"
],
"id": "7oPftvlwr6VrsViSDV7fJY",
"image_url": "https://i.scdn.co/image/ab6761610000e5eb6ff0cd5ef2ecf733804984bb",
"name": "Green Day",
"popularity": 85
},
"downloads": [
{
"virtualPlaylistId": "artist_album_7oPftvlwr6VrsViSDV7fJY_4zKDEETipertKv25vuieqG",
"album": {
"album_type": "album",
"external_urls": {
"spotify": "https://open.spotify.com/album/4zKDEETipertKv25vuieqG"
},
"id": "4zKDEETipertKv25vuieqG",
"image_url": "https://i.scdn.co/image/ab67616d0000b273f0541c8b9a3d4c77549d1d64",
"name": "Saviors (\u00e9dition de luxe)",
"release_date": "2025-08-29",
"total_tracks": 24
},
"albumType": "albums",
"status": "in_progress",
"startTime": "2025-09-23T00:44:47.580Z"
}
],
"hasCompletedDownloads": false
}
},
"timestamp": "2025-09-22T17:49:06.009684",
"snapshot_id": "20250922_174906"
}

@ -2923,6 +2923,108 @@ let missingTracks = [];
// New variables for enhanced modal functionality
let currentDownloadBatchId = null;
// ===============================
// HERO SECTION HELPER FUNCTIONS
// ===============================
/**
* Generate hero section HTML for download missing tracks modal
* Context-aware display based on available data
*/
function generateDownloadModalHeroSection(context) {
const { type, playlist, artist, album, trackCount } = context;
let heroContent = '';
let heroBackgroundImage = '';
switch (type) {
case 'artist_album':
// Artist album context - show artist + album images
const artistImage = artist?.image_url || artist?.images?.[0]?.url;
const albumImage = album?.image_url || album?.images?.[0]?.url;
// Use album image as background if available
if (albumImage) {
heroBackgroundImage = `<div class="download-missing-modal-hero-bg" style="background-image: url('${albumImage}');"></div>`;
}
heroContent = `
<div class="download-missing-modal-hero-content">
<div class="download-missing-modal-hero-images">
${artistImage ? `<img class="download-missing-modal-hero-image artist" src="${artistImage}" alt="${escapeHtml(artist.name)}">` : ''}
${albumImage ? `<img class="download-missing-modal-hero-image album" src="${albumImage}" alt="${escapeHtml(album.name)}">` : ''}
</div>
<div class="download-missing-modal-hero-metadata">
<h1 class="download-missing-modal-hero-title">${escapeHtml(album.name || 'Unknown Album')}</h1>
<div class="download-missing-modal-hero-subtitle">by ${escapeHtml(artist.name || 'Unknown Artist')}</div>
<div class="download-missing-modal-hero-details">
<span class="download-missing-modal-hero-detail">${album.album_type || 'Album'}</span>
<span class="download-missing-modal-hero-detail">${trackCount} tracks</span>
</div>
</div>
</div>
`;
break;
case 'playlist':
// Playlist context - show playlist info
heroContent = `
<div class="download-missing-modal-hero-content">
<div class="download-missing-modal-hero-icon">🎵</div>
<div class="download-missing-modal-hero-metadata">
<h1 class="download-missing-modal-hero-title">${escapeHtml(playlist.name)}</h1>
<div class="download-missing-modal-hero-subtitle">by ${escapeHtml(playlist.owner || 'Spotify')}</div>
<div class="download-missing-modal-hero-details">
<span class="download-missing-modal-hero-detail">Playlist</span>
<span class="download-missing-modal-hero-detail">${trackCount} tracks</span>
</div>
</div>
</div>
`;
break;
case 'wishlist':
// Wishlist context - show wishlist icon
heroContent = `
<div class="download-missing-modal-hero-content">
<div class="download-missing-modal-hero-icon">👁</div>
<div class="download-missing-modal-hero-metadata">
<h1 class="download-missing-modal-hero-title">Wishlist</h1>
<div class="download-missing-modal-hero-subtitle">From watched artists</div>
<div class="download-missing-modal-hero-details">
<span class="download-missing-modal-hero-detail">Wishlist</span>
<span class="download-missing-modal-hero-detail">${trackCount} tracks</span>
</div>
</div>
</div>
`;
break;
default:
// Fallback - basic display
heroContent = `
<div class="download-missing-modal-hero-content">
<div class="download-missing-modal-hero-icon">📥</div>
<div class="download-missing-modal-hero-metadata">
<h1 class="download-missing-modal-hero-title">Download Missing Tracks</h1>
<div class="download-missing-modal-hero-subtitle">${trackCount} tracks</div>
</div>
</div>
`;
break;
}
return `
<div class="download-missing-modal-hero">
${heroBackgroundImage}
${heroContent}
</div>
<div class="download-missing-modal-header-actions">
<span class="download-missing-modal-close" onclick="closeDownloadMissingModal('${context.playlistId || 'unknown'}')">&times;</span>
</div>
`;
}
let modalDownloadPoller = null;
let currentModalPlaylistId = null;
@ -2987,11 +3089,18 @@ async function openDownloadMissingModal(playlistId) {
tracks: tracks
};
// Generate hero section for playlist context
const heroContext = {
type: 'playlist',
playlist: playlist,
trackCount: tracks.length,
playlistId: playlistId
};
modal.innerHTML = `
<div class="download-missing-modal-content">
<div class="download-missing-modal-content" data-context="playlist">
<div class="download-missing-modal-header">
<h2 class="download-missing-modal-title">Download Missing Tracks - ${escapeHtml(playlist.name)}</h2>
<span class="download-missing-modal-close" onclick="closeDownloadMissingModal('${playlistId}')">&times;</span>
${generateDownloadModalHeroSection(heroContext)}
</div>
<div class="download-missing-modal-body">
@ -3104,19 +3213,19 @@ async function openDownloadMissingModalForYouTube(virtualPlaylistId, playlistNam
}
console.log(`📥 Opening Download Missing Tracks modal for YouTube playlist: ${virtualPlaylistId}`);
// Create virtual playlist object for compatibility with existing modal logic
const virtualPlaylist = {
id: virtualPlaylistId,
name: playlistName,
track_count: spotifyTracks.length
};
// Store the tracks in the cache for the modal to use
playlistTrackCache[virtualPlaylistId] = spotifyTracks;
currentPlaylistTracks = spotifyTracks;
currentModalPlaylistId = virtualPlaylistId;
let modal = document.createElement('div');
modal.id = `download-missing-modal-${virtualPlaylistId}`;
modal.className = 'download-missing-modal';
@ -3132,13 +3241,20 @@ async function openDownloadMissingModalForYouTube(virtualPlaylistId, playlistNam
playlist: virtualPlaylist,
tracks: spotifyTracks
};
// Generate hero section for YouTube playlist context
const heroContext = {
type: 'playlist',
playlist: { name: playlistName, owner: 'YouTube' },
trackCount: spotifyTracks.length,
playlistId: virtualPlaylistId
};
// Use the exact same modal HTML structure as the existing Spotify modal
modal.innerHTML = `
<div class="download-missing-modal-content">
<div class="download-missing-modal-content" data-context="playlist">
<div class="download-missing-modal-header">
<h2 class="download-missing-modal-title">Download Missing Tracks - ${escapeHtml(playlistName)}</h2>
<span class="download-missing-modal-close" onclick="closeDownloadMissingModal('${virtualPlaylistId}')">&times;</span>
${generateDownloadModalHeroSection(heroContext)}
</div>
<div class="download-missing-modal-body">
@ -3430,12 +3546,18 @@ async function openDownloadMissingWishlistModal() {
playlist: { id: playlistId, name: "Wishlist" }, // Create a pseudo-playlist object
tracks: tracks
};
// Generate hero section for wishlist context
const heroContext = {
type: 'wishlist',
trackCount: tracks.length,
playlistId: playlistId
};
modal.innerHTML = `
<div class="download-missing-modal-content">
<div class="download-missing-modal-content" data-context="wishlist">
<div class="download-missing-modal-header">
<h2 class="download-missing-modal-title">Download Missing Tracks - Wishlist</h2>
<span class="download-missing-modal-close" onclick="closeDownloadMissingModal('${playlistId}')">&times;</span>
${generateDownloadModalHeroSection(heroContext)}
</div>
<div class="download-missing-modal-body">
@ -11452,12 +11574,20 @@ async function openDownloadMissingModalForArtistAlbum(virtualPlaylistId, playlis
albumType: album.album_type
};
// Generate hero section for artist album context
const heroContext = {
type: 'artist_album',
artist: artist,
album: album,
trackCount: spotifyTracks.length,
playlistId: virtualPlaylistId
};
// Use the exact same modal HTML structure as the existing modals
modal.innerHTML = `
<div class="download-missing-modal-content">
<div class="download-missing-modal-content" data-context="artist_album">
<div class="download-missing-modal-header">
<h2 class="download-missing-modal-title">Download Missing Tracks - ${escapeHtml(playlistName)}</h2>
<span class="download-missing-modal-close" onclick="closeDownloadMissingModal('${virtualPlaylistId}')">&times;</span>
${generateDownloadModalHeroSection(heroContext)}
</div>
<div class="download-missing-modal-body">

@ -5442,48 +5442,638 @@ body {
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(8px);
z-index: 1000;
background: rgba(18, 18, 18, 0.85);
backdrop-filter: blur(12px);
z-index: 10000;
align-items: center;
justify-content: center;
opacity: 1;
transition: opacity 0.4s ease-in-out;
}
.download-missing-modal-content {
background: #1e1e1e;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
/* Premium glassmorphic foundation matching playlist modal */
background: linear-gradient(135deg,
rgba(20, 20, 20, 0.95) 0%,
rgba(12, 12, 12, 0.98) 100%);
backdrop-filter: blur(20px) saturate(1.2);
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.12);
border-top: 1px solid rgba(255, 255, 255, 0.18);
/* Premium shadow effect */
box-shadow:
0 25px 80px rgba(0, 0, 0, 0.7),
0 10px 40px rgba(0, 0, 0, 0.5),
0 0 50px rgba(29, 185, 84, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.15);
width: 1400px;
height: 900px;
max-width: 90vw;
max-height: 90vh;
max-width: 95vw;
max-height: 95vh;
display: flex;
flex-direction: column;
overflow: hidden;
/* Smooth animation */
transform: scale(1);
transition: transform 0.4s ease-in-out;
}
.download-missing-modal-header {
background: linear-gradient(135deg, #2d2d2d 0%, #1a1a1a 100%);
border-bottom: 1px solid #404040;
padding: 20px 25px;
/* Enhanced gradient with subtle transparency and glow */
background: linear-gradient(135deg,
rgba(45, 45, 45, 0.8) 0%,
rgba(26, 26, 26, 0.9) 100%);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
padding: 0;
display: flex;
flex-direction: column;
overflow: hidden;
/* Subtle inner glow */
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* Hero Section for Context-Aware Display */
.download-missing-modal-hero {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 28px;
gap: 20px;
position: relative;
min-height: 110px;
}
/* Hero Background Image (for artist/album context) */
.download-missing-modal-hero-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-size: cover;
background-position: center;
opacity: 0.15;
filter: blur(8px);
z-index: 1;
}
.download-missing-modal-hero-bg::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg,
rgba(0, 0, 0, 0.7) 0%,
rgba(0, 0, 0, 0.3) 100%);
}
/* Hero Content */
.download-missing-modal-hero-content {
display: flex;
align-items: center;
gap: 20px;
position: relative;
z-index: 2;
flex: 1;
}
/* Hero Images Container */
.download-missing-modal-hero-images {
display: flex;
align-items: center;
gap: 16px;
flex-shrink: 0;
}
.download-missing-modal-hero-image {
width: 80px;
height: 80px;
border-radius: 12px;
object-fit: cover;
box-shadow:
0 8px 24px rgba(0, 0, 0, 0.4),
0 4px 12px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.download-missing-modal-hero-image:hover {
transform: scale(1.05);
box-shadow:
0 12px 32px rgba(0, 0, 0, 0.5),
0 6px 16px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
/* Artist Image (circular) */
.download-missing-modal-hero-image.artist {
border-radius: 50%;
width: 70px;
height: 70px;
}
/* Album Image (rounded square) */
.download-missing-modal-hero-image.album {
border-radius: 12px;
width: 80px;
height: 80px;
}
/* Hero Icon (for wishlist/playlist without images) */
.download-missing-modal-hero-icon {
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg,
rgba(29, 185, 84, 0.2) 0%,
rgba(30, 215, 96, 0.1) 100%);
border-radius: 12px;
font-size: 40px;
color: #1ed760;
border: 1px solid rgba(29, 185, 84, 0.3);
box-shadow:
0 8px 24px rgba(29, 185, 84, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* Hero Metadata */
.download-missing-modal-hero-metadata {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
}
.download-missing-modal-hero-title {
color: #ffffff;
font-size: 22px;
font-weight: 700;
margin: 0;
line-height: 1.2;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.download-missing-modal-hero-subtitle {
color: #e0e0e0;
font-size: 16px;
font-weight: 500;
margin: 0;
opacity: 0.9;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.download-missing-modal-hero-details {
display: flex;
gap: 16px;
font-size: 14px;
margin-top: 4px;
}
.download-missing-modal-hero-detail {
color: #1ed760;
font-weight: 600;
padding: 4px 8px;
background: rgba(29, 185, 84, 0.1);
border-radius: 6px;
border: 1px solid rgba(29, 185, 84, 0.2);
text-transform: uppercase;
letter-spacing: 0.5px;
font-size: 12px;
}
/* Header Actions (close button) */
.download-missing-modal-header-actions {
position: relative;
z-index: 3;
padding: 16px 24px 0 0;
display: flex;
justify-content: flex-end;
}
/* ===============================
CONTEXT-SPECIFIC STYLING
=============================== */
/* Artist Album Context - Music theme with artist/album colors */
.download-missing-modal-content[data-context="artist_album"] .download-missing-modal-hero-bg {
opacity: 0.2;
}
.download-missing-modal-content[data-context="artist_album"] .download-missing-modal-hero {
background: linear-gradient(135deg,
rgba(45, 45, 45, 0.9) 0%,
rgba(26, 26, 26, 0.95) 100%);
}
.download-missing-modal-content[data-context="artist_album"] .download-missing-modal-hero-detail {
background: rgba(29, 185, 84, 0.15);
border-color: rgba(29, 185, 84, 0.3);
box-shadow: 0 0 10px rgba(29, 185, 84, 0.2);
}
/* Playlist Context - Spotify green theme */
.download-missing-modal-content[data-context="playlist"] .download-missing-modal-hero {
background: linear-gradient(135deg,
rgba(29, 185, 84, 0.1) 0%,
rgba(26, 26, 26, 0.95) 100%);
}
.download-missing-modal-content[data-context="playlist"] .download-missing-modal-hero-icon {
background: linear-gradient(135deg,
rgba(29, 185, 84, 0.25) 0%,
rgba(30, 215, 96, 0.15) 100%);
border-color: rgba(29, 185, 84, 0.4);
box-shadow: 0 8px 24px rgba(29, 185, 84, 0.25);
}
.download-missing-modal-content[data-context="playlist"] .download-missing-modal-hero-detail {
background: rgba(29, 185, 84, 0.12);
border-color: rgba(29, 185, 84, 0.25);
}
/* Wishlist Context - Eye/watch theme with purple accents */
.download-missing-modal-content[data-context="wishlist"] .download-missing-modal-hero {
background: linear-gradient(135deg,
rgba(123, 31, 162, 0.1) 0%,
rgba(26, 26, 26, 0.95) 100%);
}
.download-missing-modal-content[data-context="wishlist"] .download-missing-modal-hero-icon {
background: linear-gradient(135deg,
rgba(123, 31, 162, 0.25) 0%,
rgba(142, 36, 170, 0.15) 100%);
border-color: rgba(123, 31, 162, 0.4);
color: #DA70D6;
box-shadow: 0 8px 24px rgba(123, 31, 162, 0.25);
}
.download-missing-modal-content[data-context="wishlist"] .download-missing-modal-hero-detail {
background: rgba(123, 31, 162, 0.12);
border-color: rgba(123, 31, 162, 0.25);
color: #DA70D6;
}
.download-missing-modal-content[data-context="wishlist"] .stat-total .dashboard-stat-number {
color: #DA70D6;
text-shadow: 0 0 20px rgba(218, 112, 214, 0.4);
}
/* ===============================
ANIMATIONS & POLISH
=============================== */
/* Modal entrance animation */
@keyframes modalFadeIn {
0% {
opacity: 0;
transform: scale(0.9) translateY(20px);
filter: blur(4px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
filter: blur(0px);
}
}
@keyframes modalOverlayFadeIn {
0% {
opacity: 0;
backdrop-filter: blur(0px);
}
100% {
opacity: 1;
backdrop-filter: blur(12px);
}
}
/* Apply entrance animations */
#download-missing-modal:not(.hidden) {
animation: modalOverlayFadeIn 0.4s ease-out;
}
#download-missing-modal:not(.hidden) .download-missing-modal-content {
animation: modalFadeIn 0.4s ease-out;
}
/* Hero section animations */
.download-missing-modal-hero-image {
animation: imageSlideIn 0.6s ease-out 0.2s both;
}
@keyframes imageSlideIn {
0% {
opacity: 0;
transform: translateX(-20px) scale(0.8);
}
100% {
opacity: 1;
transform: translateX(0) scale(1);
}
}
.download-missing-modal-hero-metadata {
animation: textSlideIn 0.6s ease-out 0.3s both;
}
@keyframes textSlideIn {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.download-missing-modal-hero-detail {
animation: detailPop 0.4s ease-out 0.5s both;
}
@keyframes detailPop {
0% {
opacity: 0;
transform: scale(0.8);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* Stats dashboard animation */
.dashboard-stat {
animation: statSlideUp 0.5s ease-out calc(0.4s + var(--stat-index, 0) * 0.1s) both;
}
@keyframes statSlideUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Progress section animation */
.progress-item {
animation: progressSlideIn 0.5s ease-out calc(0.6s + var(--progress-index, 0) * 0.1s) both;
}
@keyframes progressSlideIn {
0% {
opacity: 0;
transform: translateX(-20px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
/* Enhanced hover effects */
.dashboard-stat:hover .dashboard-stat-number {
transform: scale(1.1);
filter: brightness(1.2);
}
.download-missing-modal-hero-detail:hover {
transform: translateY(-2px) scale(1.05);
box-shadow: 0 4px 12px rgba(29, 185, 84, 0.3);
}
/* Subtle pulse animation for active elements */
.progress-fill {
position: relative;
overflow: hidden;
}
.progress-fill::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent 0%,
rgba(255, 255, 255, 0.3) 50%,
transparent 100%);
animation: progressShimmer 2s infinite;
}
@keyframes progressShimmer {
0% {
left: -100%;
}
100% {
left: 100%;
}
}
/* ===============================
RESPONSIVE DESIGN
=============================== */
/* Large screens (1600px+) */
@media (min-width: 1600px) {
.download-missing-modal-content {
width: 1500px;
height: 950px;
}
.download-missing-modal-hero {
min-height: 140px;
padding: 28px 32px;
}
.download-missing-modal-hero-image.album {
width: 90px;
height: 90px;
}
.download-missing-modal-hero-image.artist {
width: 80px;
height: 80px;
}
}
/* Medium screens (1200px - 1599px) */
@media (max-width: 1599px) and (min-width: 1200px) {
.download-missing-modal-content {
width: 1200px;
height: 800px;
}
}
/* Small screens (768px - 1199px) */
@media (max-width: 1199px) and (min-width: 768px) {
.download-missing-modal-content {
width: 95vw;
height: 90vh;
max-width: 1000px;
}
.download-dashboard-stats {
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.download-missing-modal-hero {
min-height: 100px;
padding: 20px 24px;
}
.download-missing-modal-hero-image.album {
width: 70px;
height: 70px;
}
.download-missing-modal-hero-image.artist {
width: 60px;
height: 60px;
}
.download-missing-modal-hero-icon {
width: 70px;
height: 70px;
font-size: 32px;
}
.download-missing-modal-hero-title {
font-size: 20px;
}
.download-missing-modal-hero-subtitle {
font-size: 14px;
}
}
/* Mobile screens (767px and below) */
@media (max-width: 767px) {
.download-missing-modal-content {
width: 95vw;
height: 95vh;
max-width: none;
border-radius: 16px;
}
.download-missing-modal-hero {
flex-direction: column;
text-align: center;
min-height: auto;
padding: 20px;
gap: 16px;
}
.download-missing-modal-hero-content {
flex-direction: column;
gap: 16px;
}
.download-missing-modal-hero-images {
justify-content: center;
}
.download-missing-modal-hero-image.album,
.download-missing-modal-hero-image.artist {
width: 60px;
height: 60px;
}
.download-missing-modal-hero-icon {
width: 60px;
height: 60px;
font-size: 28px;
margin: 0 auto;
}
.download-missing-modal-hero-title {
font-size: 18px;
white-space: normal;
text-align: center;
}
.download-missing-modal-hero-subtitle {
font-size: 14px;
white-space: normal;
text-align: center;
}
.download-missing-modal-hero-details {
justify-content: center;
flex-wrap: wrap;
}
.download-missing-modal-body {
padding: 0 20px 20px 20px;
gap: 20px;
}
.download-dashboard-stats {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
padding: 20px;
}
.dashboard-stat-number {
font-size: 28px;
}
.dashboard-stat-label {
font-size: 12px;
}
.download-missing-modal-footer {
padding: 20px;
flex-direction: column;
gap: 16px;
}
.download-phase-controls {
width: 100%;
justify-content: center;
}
.download-control-btn {
flex: 1;
max-width: 200px;
}
}
/* Legacy title styling - now handled by hero section */
.download-missing-modal-title {
color: #1db954;
color: #ffffff;
font-size: 18px;
font-weight: 700;
margin: 0;
line-height: 1.2;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.download-missing-modal-close {
color: #cccccc;
font-size: 32px;
color: #b3b3b3;
font-size: 28px;
font-weight: 300;
cursor: pointer;
transition: all 0.2s ease;
transition: all 0.3s ease;
line-height: 1;
width: 40px;
height: 40px;
@ -5491,124 +6081,226 @@ body {
align-items: center;
justify-content: center;
border-radius: 50%;
padding: 4px;
margin: -4px;
}
.download-missing-modal-close:hover {
color: #ffffff;
background: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.15);
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.download-missing-modal-body {
flex: 1;
display: flex;
flex-direction: column;
padding: 25px;
padding: 16px 28px 25px 28px;
gap: 20px;
overflow: hidden;
}
/* Dashboard Stats Section */
.download-dashboard-stats {
background: #2d2d2d;
border: 1px solid #444444;
border-radius: 12px;
padding: 20px;
/* Premium glassmorphic card */
background: linear-gradient(135deg,
rgba(35, 35, 35, 0.7) 0%,
rgba(25, 25, 25, 0.8) 100%);
backdrop-filter: blur(10px) saturate(1.1);
border: 1px solid rgba(255, 255, 255, 0.1);
border-top: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 24px;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
gap: 24px;
/* Premium shadow with subtle glow */
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 4px 16px rgba(0, 0, 0, 0.2),
0 0 20px rgba(29, 185, 84, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.dashboard-stat {
text-align: center;
padding: 8px;
border-radius: 12px;
transition: all 0.3s ease;
}
.dashboard-stat:hover {
background: rgba(255, 255, 255, 0.05);
transform: translateY(-2px);
}
.dashboard-stat-number {
font-size: 28px;
font-size: 32px;
font-weight: 700;
margin-bottom: 5px;
margin-bottom: 8px;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.dashboard-stat-label {
font-size: 13px;
color: #cccccc;
font-weight: 500;
font-size: 14px;
color: #e0e0e0;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
opacity: 0.9;
}
/* Enhanced stat colors with glow effects */
.stat-total .dashboard-stat-number {
color: #1ed760;
text-shadow: 0 0 20px rgba(30, 215, 96, 0.4);
}
.stat-total .dashboard-stat-number { color: #1db954; }
.stat-found .dashboard-stat-number { color: #4CAF50; }
.stat-missing .dashboard-stat-number { color: #FF6B35; }
.stat-downloaded .dashboard-stat-number { color: #2196F3; }
.stat-found .dashboard-stat-number {
color: #4CAF50;
text-shadow: 0 0 20px rgba(76, 175, 80, 0.4);
}
.stat-missing .dashboard-stat-number {
color: #FF6B35;
text-shadow: 0 0 20px rgba(255, 107, 53, 0.4);
}
.stat-downloaded .dashboard-stat-number {
color: #2196F3;
text-shadow: 0 0 20px rgba(33, 150, 243, 0.4);
}
/* Progress Section */
.download-progress-section {
background: #2d2d2d;
border: 1px solid #444444;
border-radius: 12px;
/* Premium glassmorphic card matching stats */
background: linear-gradient(135deg,
rgba(35, 35, 35, 0.7) 0%,
rgba(25, 25, 25, 0.8) 100%);
backdrop-filter: blur(10px) saturate(1.1);
border: 1px solid rgba(255, 255, 255, 0.1);
border-top: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 15px;
gap: 16px;
/* Premium shadow with subtle glow */
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 4px 16px rgba(0, 0, 0, 0.2),
0 0 20px rgba(29, 185, 84, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.progress-item {
display: flex;
flex-direction: column;
gap: 6px;
gap: 8px;
padding: 2px 0;
}
.progress-label {
font-size: 13px;
font-size: 14px;
font-weight: 600;
color: #cccccc;
color: #e8e8e8;
display: flex;
align-items: center;
gap: 8px;
gap: 10px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.progress-bar {
background: #404040;
border-radius: 8px;
height: 8px;
background: rgba(64, 64, 64, 0.8);
border-radius: 10px;
height: 10px;
overflow: hidden;
box-shadow:
inset 0 2px 4px rgba(0, 0, 0, 0.3),
0 1px 2px rgba(0, 0, 0, 0.2);
}
.progress-fill {
background: linear-gradient(90deg, #1db954, #1ed760);
height: 100%;
border-radius: 8px;
transition: width 0.3s ease;
border-radius: 10px;
transition: width 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
width: 0%;
box-shadow:
0 0 15px rgba(29, 185, 84, 0.6),
inset 0 1px 2px rgba(255, 255, 255, 0.3);
position: relative;
}
.progress-fill.analysis { background: linear-gradient(90deg, #2196F3, #21CBF3); }
.progress-fill.download { background: linear-gradient(90deg, #FF6B35, #FF8A35); }
.progress-fill::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 50%;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, transparent 100%);
border-radius: 10px 10px 0 0;
}
.progress-fill.analysis {
background: linear-gradient(90deg, #2196F3, #21CBF3);
box-shadow:
0 0 15px rgba(33, 150, 243, 0.6),
inset 0 1px 2px rgba(255, 255, 255, 0.3);
}
.progress-fill.download {
background: linear-gradient(90deg, #FF6B35, #FF8A35);
box-shadow:
0 0 15px rgba(255, 107, 53, 0.6),
inset 0 1px 2px rgba(255, 255, 255, 0.3);
}
/* Track Table Section */
.download-tracks-section {
background: #2d2d2d;
border: 1px solid #444444;
border-radius: 12px;
/* Premium glassmorphic card matching other sections */
background: linear-gradient(135deg,
rgba(35, 35, 35, 0.7) 0%,
rgba(25, 25, 25, 0.8) 100%);
backdrop-filter: blur(10px) saturate(1.1);
border: 1px solid rgba(255, 255, 255, 0.1);
border-top: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
/* Premium shadow with subtle glow */
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 4px 16px rgba(0, 0, 0, 0.2),
0 0 20px rgba(29, 185, 84, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.download-tracks-header {
padding: 15px 20px;
border-bottom: 1px solid #444444;
background: #333333;
padding: 18px 24px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: linear-gradient(135deg,
rgba(40, 40, 40, 0.8) 0%,
rgba(30, 30, 30, 0.9) 100%);
}
.download-tracks-title {
font-size: 15px;
font-weight: 600;
font-size: 16px;
font-weight: 700;
color: #ffffff;
margin: 0;
display: flex;
align-items: center;
gap: 8px;
gap: 10px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.download-tracks-table-container {
@ -5730,12 +6422,17 @@ body {
/* Modal Footer */
.download-missing-modal-footer {
background: #2a2a2a;
border-top: 1px solid #404040;
padding: 20px 25px;
background: linear-gradient(135deg,
rgba(42, 42, 42, 0.9) 0%,
rgba(30, 30, 30, 0.95) 100%);
border-top: 1px solid rgba(255, 255, 255, 0.08);
padding: 24px 28px;
display: flex;
align-items: center;
justify-content: space-between;
/* Subtle inner glow */
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.download-phase-controls {

Loading…
Cancel
Save