Spaces:
Running
Running
<html> | |
<head> | |
<title>Music Player</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
<style> | |
:root { | |
--bg-primary: #0f0f0f; | |
--bg-secondary: #1a1a1a; | |
--bg-tertiary: #2a2a2a; | |
--bg-hover: #3a3a3a; | |
--accent-primary: #ff6b6b; | |
--accent-secondary: #4ecdc4; | |
--text-primary: #ffffff; | |
--text-secondary: #b3b3b3; | |
--text-muted: #666666; | |
--border-color: #333333; | |
--success: #4caf50; | |
--warning: #ff9800; | |
--error: #f44336; | |
--gradient-primary: linear-gradient(135deg, #ff6b6b 0%, #4ecdc4 100%); | |
--gradient-secondary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3); | |
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); | |
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5); | |
--border-radius: 12px; | |
--border-radius-sm: 8px; | |
--border-radius-lg: 16px; | |
} | |
* { | |
box-sizing: border-box; | |
-webkit-tap-highlight-color: transparent; | |
margin: 0; | |
padding: 0; | |
} | |
body { | |
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
background: var(--bg-primary); | |
color: var(--text-primary); | |
line-height: 1.6; | |
min-height: 100vh; | |
overflow-x: hidden; | |
} | |
/* Background Animation */ | |
body::before { | |
content: ''; | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: radial-gradient(circle at 20% 50%, rgba(255, 107, 107, 0.1) 0%, transparent 50%), | |
radial-gradient(circle at 80% 20%, rgba(78, 205, 196, 0.1) 0%, transparent 50%), | |
radial-gradient(circle at 40% 80%, rgba(102, 126, 234, 0.1) 0%, transparent 50%); | |
z-index: -1; | |
animation: backgroundShift 20s ease-in-out infinite; | |
} | |
@keyframes backgroundShift { | |
0%, 100% { opacity: 1; } | |
50% { opacity: 0.7; } | |
} | |
.sw-container { | |
max-width: 800px; | |
margin: 0 auto; | |
padding: 20px; | |
min-height: 100vh; | |
} | |
.sw-player { | |
background: var(--bg-secondary); | |
border-radius: var(--border-radius-lg); | |
box-shadow: var(--shadow-lg); | |
padding: 32px; | |
margin-bottom: 24px; | |
backdrop-filter: blur(10px); | |
border: 1px solid var(--border-color); | |
} | |
.sw-header { | |
font-size: 2.5rem; | |
font-weight: 700; | |
text-align: center; | |
margin-bottom: 32px; | |
background: var(--gradient-primary); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
background-clip: text; | |
position: relative; | |
} | |
.sw-header::after { | |
content: ''; | |
position: absolute; | |
bottom: -12px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 60px; | |
height: 3px; | |
background: var(--gradient-primary); | |
border-radius: 2px; | |
} | |
.sw-now-playing { | |
text-align: center; | |
margin-bottom: 24px; | |
padding: 16px; | |
background: var(--bg-tertiary); | |
border-radius: var(--border-radius); | |
border: 1px solid var(--border-color); | |
} | |
.sw-now-playing-label { | |
font-size: 0.85rem; | |
color: var(--text-muted); | |
text-transform: uppercase; | |
letter-spacing: 0.5px; | |
margin-bottom: 8px; | |
} | |
.sw-now-playing-song { | |
font-size: 1.1rem; | |
font-weight: 600; | |
color: var(--text-primary); | |
white-space: nowrap; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
} | |
.sw-custom-player { | |
margin: 24px 0; | |
padding: 20px; | |
background: var(--bg-tertiary); | |
border-radius: var(--border-radius); | |
border: 1px solid var(--border-color); | |
} | |
.sw-progress-container { | |
margin-bottom: 20px; | |
} | |
.sw-time-display { | |
display: flex; | |
justify-content: space-between; | |
font-size: 0.85rem; | |
color: var(--text-secondary); | |
margin-bottom: 8px; | |
} | |
.sw-progress-bar { | |
position: relative; | |
height: 6px; | |
background: var(--border-color); | |
border-radius: 3px; | |
cursor: pointer; | |
overflow: hidden; | |
} | |
.sw-progress-fill { | |
height: 100%; | |
background: var(--gradient-primary); | |
width: 0%; | |
transition: width 0.1s ease; | |
border-radius: 3px; | |
} | |
.sw-progress-handle { | |
position: absolute; | |
top: 50%; | |
left: 0%; | |
width: 16px; | |
height: 16px; | |
background: var(--accent-primary); | |
border-radius: 50%; | |
transform: translate(-50%, -50%); | |
cursor: pointer; | |
opacity: 0; | |
transition: opacity 0.2s ease; | |
box-shadow: var(--shadow-sm); | |
} | |
.sw-progress-bar:hover .sw-progress-handle { | |
opacity: 1; | |
} | |
.sw-audio-controls { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
gap: 20px; | |
} | |
.sw-volume-control { | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
flex: 1; | |
} | |
.sw-volume-control i { | |
color: var(--text-secondary); | |
font-size: 1.1rem; | |
min-width: 20px; | |
} | |
.sw-volume-slider { | |
flex: 1; | |
max-width: 120px; | |
} | |
.sw-slider { | |
width: 100%; | |
height: 4px; | |
border-radius: 2px; | |
background: var(--border-color); | |
outline: none; | |
-webkit-appearance: none; | |
appearance: none; | |
} | |
.sw-slider::-webkit-slider-thumb { | |
-webkit-appearance: none; | |
appearance: none; | |
width: 16px; | |
height: 16px; | |
border-radius: 50%; | |
background: var(--accent-primary); | |
cursor: pointer; | |
box-shadow: var(--shadow-sm); | |
} | |
.sw-slider::-moz-range-thumb { | |
width: 16px; | |
height: 16px; | |
border-radius: 50%; | |
background: var(--accent-primary); | |
cursor: pointer; | |
border: none; | |
box-shadow: var(--shadow-sm); | |
} | |
.sw-slider::-webkit-slider-track { | |
background: var(--gradient-primary); | |
height: 4px; | |
border-radius: 2px; | |
} | |
.sw-slider::-moz-range-track { | |
background: var(--gradient-primary); | |
height: 4px; | |
border-radius: 2px; | |
border: none; | |
} | |
#swVolumeValue { | |
font-size: 0.85rem; | |
color: var(--text-secondary); | |
min-width: 40px; | |
} | |
.sw-speed-control { | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.sw-speed-control i { | |
color: var(--text-secondary); | |
font-size: 1.1rem; | |
} | |
.sw-speed-dropdown { | |
background: var(--bg-primary); | |
color: var(--text-primary); | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius-sm); | |
padding: 6px 12px; | |
font-size: 0.85rem; | |
cursor: pointer; | |
outline: none; | |
} | |
.sw-speed-dropdown:hover { | |
border-color: var(--accent-primary); | |
} | |
.sw-speed-dropdown:focus { | |
border-color: var(--accent-primary); | |
box-shadow: 0 0 0 2px rgba(255, 107, 107, 0.2); | |
} | |
/* Upload Section Styles */ | |
.sw-upload-section { | |
margin: 24px 0; | |
padding: 20px; | |
background: var(--bg-tertiary); | |
border-radius: var(--border-radius); | |
border: 1px solid var(--border-color); | |
} | |
.sw-upload-header { | |
font-size: 1.2rem; | |
font-weight: 600; | |
margin-bottom: 16px; | |
color: var(--text-primary); | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.sw-upload-area { | |
border: 2px dashed var(--border-color); | |
border-radius: var(--border-radius); | |
padding: 40px 20px; | |
text-align: center; | |
transition: all 0.3s ease; | |
cursor: pointer; | |
background: var(--bg-primary); | |
} | |
.sw-upload-area:hover { | |
border-color: var(--accent-primary); | |
background: var(--bg-secondary); | |
} | |
.sw-upload-area.drag-over { | |
border-color: var(--accent-primary); | |
background: var(--bg-secondary); | |
transform: scale(1.02); | |
} | |
.sw-upload-content { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
gap: 12px; | |
} | |
.sw-upload-icon { | |
font-size: 3rem; | |
color: var(--text-muted); | |
margin-bottom: 8px; | |
} | |
.sw-upload-text { | |
font-size: 1.1rem; | |
color: var(--text-primary); | |
margin: 0; | |
} | |
.sw-upload-subtext { | |
font-size: 0.9rem; | |
color: var(--text-muted); | |
margin: 0; | |
} | |
.sw-upload-btn { | |
margin: 8px 0; | |
} | |
.sw-upload-info { | |
font-size: 0.8rem; | |
color: var(--text-muted); | |
margin: 8px 0 0 0; | |
} | |
.sw-upload-progress { | |
margin-top: 16px; | |
} | |
.sw-progress-item { | |
background: var(--bg-primary); | |
border-radius: var(--border-radius-sm); | |
padding: 12px; | |
border: 1px solid var(--border-color); | |
} | |
.sw-progress-info { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 8px; | |
font-size: 0.9rem; | |
} | |
.sw-progress-bar-upload { | |
height: 6px; | |
background: var(--border-color); | |
border-radius: 3px; | |
overflow: hidden; | |
} | |
.sw-progress-fill-upload { | |
height: 100%; | |
background: var(--gradient-primary); | |
width: 0%; | |
transition: width 0.3s ease; | |
border-radius: 3px; | |
} | |
.sw-upload-success { | |
color: var(--success); | |
font-weight: 500; | |
} | |
.sw-upload-error { | |
color: var(--error); | |
font-weight: 500; | |
} | |
/* Advanced Playlist Management Styles */ | |
.sw-modal-large { | |
max-width: 700px; | |
max-height: 90vh; | |
} | |
.sw-playlist-editor { | |
margin-bottom: 24px; | |
padding: 16px; | |
background: var(--bg-primary); | |
border-radius: var(--border-radius); | |
border: 1px solid var(--border-color); | |
} | |
.sw-label { | |
display: block; | |
font-size: 0.9rem; | |
color: var(--text-secondary); | |
margin-bottom: 8px; | |
font-weight: 500; | |
} | |
.sw-name-editor { | |
display: flex; | |
gap: 12px; | |
align-items: center; | |
} | |
.sw-name-editor .sw-input { | |
flex: 1; | |
margin-bottom: 0; | |
} | |
.sw-btn-small { | |
padding: 8px 16px; | |
font-size: 0.85rem; | |
min-width: auto; | |
} | |
.sw-manage-section { | |
margin-bottom: 24px; | |
} | |
.sw-manage-section h3 { | |
font-size: 1.1rem; | |
color: var(--text-primary); | |
margin-bottom: 12px; | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.sw-song-count { | |
font-size: 0.9rem; | |
color: var(--text-muted); | |
font-weight: normal; | |
} | |
.sw-available-songs, .sw-current-songs { | |
max-height: 200px; | |
overflow-y: auto; | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
background: var(--bg-primary); | |
} | |
.sw-song-item { | |
padding: 12px 16px; | |
border-bottom: 1px solid var(--border-color); | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
transition: background 0.2s ease; | |
cursor: pointer; | |
} | |
.sw-song-item:last-child { | |
border-bottom: none; | |
} | |
.sw-song-item:hover { | |
background: var(--bg-hover); | |
} | |
.sw-song-item.dragging { | |
opacity: 0.5; | |
background: var(--bg-hover); | |
} | |
.sw-song-item.drag-over { | |
border-top: 2px solid var(--accent-primary); | |
} | |
.sw-song-info { | |
flex: 1; | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.sw-song-title { | |
font-size: 0.9rem; | |
color: var(--text-primary); | |
} | |
.sw-song-actions { | |
display: flex; | |
gap: 8px; | |
} | |
.sw-action-btn { | |
background: none; | |
border: none; | |
color: var(--text-muted); | |
cursor: pointer; | |
padding: 4px; | |
border-radius: 4px; | |
transition: all 0.2s ease; | |
font-size: 0.9rem; | |
} | |
.sw-action-btn:hover { | |
color: var(--accent-primary); | |
background: var(--bg-hover); | |
} | |
.sw-action-btn.remove { | |
color: var(--error); | |
} | |
.sw-action-btn.add { | |
color: var(--success); | |
} | |
.sw-drag-handle { | |
cursor: grab; | |
color: var(--text-muted); | |
} | |
.sw-drag-handle:active { | |
cursor: grabbing; | |
} | |
.sw-playlist-controls { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); | |
gap: 12px; | |
} | |
.sw-controls { | |
display: flex; | |
justify-content: center; | |
gap: 16px; | |
margin: 24px 0; | |
flex-wrap: wrap; | |
} | |
.sw-btn { | |
background: var(--bg-tertiary); | |
color: var(--text-primary); | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
padding: 12px 20px; | |
font-size: 0.9rem; | |
font-weight: 500; | |
font-family: inherit; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
position: relative; | |
overflow: hidden; | |
min-width: 100px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
gap: 8px; | |
} | |
.sw-btn::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: -100%; | |
width: 100%; | |
height: 100%; | |
background: var(--gradient-primary); | |
transition: left 0.3s ease; | |
z-index: 0; | |
} | |
.sw-btn span { | |
position: relative; | |
z-index: 1; | |
} | |
.sw-btn:hover::before { | |
left: 0; | |
} | |
.sw-btn:hover { | |
transform: translateY(-2px); | |
box-shadow: var(--shadow-md); | |
border-color: var(--accent-primary); | |
} | |
.sw-btn.active { | |
background: var(--gradient-primary); | |
border-color: var(--accent-primary); | |
color: white; | |
box-shadow: var(--shadow-md); | |
} | |
.sw-btn.active::before { | |
left: 0; | |
} | |
.sw-btn:active { | |
transform: translateY(0); | |
} | |
.sw-dropdown { | |
width: 100%; | |
padding: 16px; | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
font-size: 1rem; | |
font-family: inherit; | |
margin-bottom: 24px; | |
background: var(--bg-tertiary); | |
color: var(--text-primary); | |
cursor: pointer; | |
transition: all 0.3s ease; | |
} | |
.sw-dropdown:hover { | |
border-color: var(--accent-primary); | |
box-shadow: var(--shadow-sm); | |
} | |
.sw-dropdown:focus { | |
outline: none; | |
border-color: var(--accent-primary); | |
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.2); | |
} | |
/* Search and Filter Styles */ | |
.sw-search-section { | |
margin-bottom: 24px; | |
} | |
.sw-search-container { | |
position: relative; | |
margin-bottom: 16px; | |
} | |
.sw-search-input { | |
width: 100%; | |
padding: 16px 50px 16px 16px; | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
font-size: 1rem; | |
font-family: inherit; | |
background: var(--bg-tertiary); | |
color: var(--text-primary); | |
transition: all 0.3s ease; | |
} | |
.sw-search-input:focus { | |
outline: none; | |
border-color: var(--accent-primary); | |
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.2); | |
} | |
.sw-search-input::placeholder { | |
color: var(--text-muted); | |
} | |
.sw-search-clear { | |
position: absolute; | |
right: 12px; | |
top: 50%; | |
transform: translateY(-50%); | |
background: none; | |
border: none; | |
color: var(--text-muted); | |
cursor: pointer; | |
padding: 8px; | |
border-radius: 50%; | |
transition: all 0.3s ease; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.sw-search-clear:hover { | |
background: var(--bg-secondary); | |
color: var(--text-primary); | |
} | |
.sw-filter-options { | |
display: flex; | |
gap: 8px; | |
flex-wrap: wrap; | |
} | |
.sw-filter-btn { | |
padding: 8px 16px; | |
border: 1px solid var(--border-color); | |
border-radius: 20px; | |
background: var(--bg-tertiary); | |
color: var(--text-muted); | |
cursor: pointer; | |
transition: all 0.3s ease; | |
font-size: 0.9rem; | |
display: flex; | |
align-items: center; | |
gap: 6px; | |
} | |
.sw-filter-btn:hover { | |
border-color: var(--accent-primary); | |
color: var(--text-primary); | |
} | |
.sw-filter-btn.active { | |
background: var(--accent-primary); | |
border-color: var(--accent-primary); | |
color: white; | |
} | |
/* Playlist View Styles */ | |
.sw-playlist-view { | |
margin-top: 24px; | |
} | |
.sw-playlist-view-header { | |
font-size: 1.2rem; | |
font-weight: 600; | |
margin-bottom: 16px; | |
color: var(--text-primary); | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.sw-playlist-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); | |
gap: 16px; | |
} | |
.sw-playlist-card { | |
background: var(--bg-tertiary); | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
padding: 20px; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
} | |
.sw-playlist-card:hover { | |
border-color: var(--accent-primary); | |
box-shadow: var(--shadow-sm); | |
transform: translateY(-2px); | |
} | |
.sw-playlist-card-header { | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
margin-bottom: 12px; | |
} | |
.sw-playlist-card-icon { | |
width: 40px; | |
height: 40px; | |
background: var(--accent-primary); | |
border-radius: 8px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: white; | |
font-size: 1.2rem; | |
} | |
.sw-playlist-card-title { | |
font-size: 1.1rem; | |
font-weight: 600; | |
color: var(--text-primary); | |
} | |
.sw-playlist-card-info { | |
color: var(--text-muted); | |
font-size: 0.9rem; | |
margin-bottom: 16px; | |
} | |
.sw-playlist-card-actions { | |
display: flex; | |
gap: 8px; | |
} | |
.sw-playlist-card-btn { | |
padding: 8px 16px; | |
border: 1px solid var(--border-color); | |
border-radius: 20px; | |
background: var(--bg-secondary); | |
color: var(--text-primary); | |
cursor: pointer; | |
transition: all 0.3s ease; | |
font-size: 0.85rem; | |
display: flex; | |
align-items: center; | |
gap: 6px; | |
} | |
.sw-playlist-card-btn:hover { | |
border-color: var(--accent-primary); | |
background: var(--accent-primary); | |
color: white; | |
} | |
/* Queue Management Styles */ | |
.sw-queue-section { | |
margin-top: 32px; | |
background: var(--bg-tertiary); | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
overflow: hidden; | |
} | |
.sw-queue-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
padding: 16px 20px; | |
background: var(--bg-secondary); | |
border-bottom: 1px solid var(--border-color); | |
} | |
.sw-queue-title { | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
font-size: 1.1rem; | |
font-weight: 600; | |
color: var(--text-primary); | |
} | |
.sw-queue-count { | |
color: var(--text-muted); | |
font-weight: 400; | |
font-size: 0.9rem; | |
} | |
.sw-queue-controls { | |
display: flex; | |
gap: 8px; | |
} | |
.sw-queue-btn { | |
padding: 8px 16px; | |
border: 1px solid var(--border-color); | |
border-radius: 20px; | |
background: var(--bg-tertiary); | |
color: var(--text-primary); | |
cursor: pointer; | |
transition: all 0.3s ease; | |
font-size: 0.85rem; | |
display: flex; | |
align-items: center; | |
gap: 6px; | |
} | |
.sw-queue-btn:hover { | |
border-color: var(--accent-primary); | |
background: var(--accent-primary); | |
color: white; | |
} | |
.sw-queue-content { | |
max-height: 400px; | |
overflow-y: auto; | |
} | |
.sw-queue-list { | |
padding: 0; | |
} | |
.sw-queue-item { | |
display: flex; | |
align-items: center; | |
padding: 12px 20px; | |
border-bottom: 1px solid var(--border-color); | |
cursor: pointer; | |
transition: all 0.3s ease; | |
position: relative; | |
} | |
.sw-queue-item:hover { | |
background: var(--bg-secondary); | |
} | |
.sw-queue-item.current { | |
background: rgba(255, 107, 107, 0.1); | |
border-left: 4px solid var(--accent-primary); | |
} | |
.sw-queue-item.playing { | |
background: rgba(255, 107, 107, 0.15); | |
} | |
.sw-queue-item-number { | |
width: 30px; | |
text-align: center; | |
color: var(--text-muted); | |
font-size: 0.9rem; | |
margin-right: 12px; | |
} | |
.sw-queue-item.current .sw-queue-item-number { | |
color: var(--accent-primary); | |
font-weight: 600; | |
} | |
.sw-queue-item-info { | |
flex: 1; | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
} | |
.sw-queue-item-icon { | |
width: 16px; | |
text-align: center; | |
color: var(--text-muted); | |
} | |
.sw-queue-item.current .sw-queue-item-icon { | |
color: var(--accent-primary); | |
} | |
.sw-queue-item-name { | |
flex: 1; | |
color: var(--text-primary); | |
font-size: 0.95rem; | |
white-space: nowrap; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
} | |
.sw-queue-item.current .sw-queue-item-name { | |
font-weight: 600; | |
} | |
.sw-queue-item-actions { | |
display: flex; | |
gap: 8px; | |
opacity: 0; | |
transition: opacity 0.3s ease; | |
} | |
.sw-queue-item:hover .sw-queue-item-actions { | |
opacity: 1; | |
} | |
.sw-queue-item-btn { | |
width: 32px; | |
height: 32px; | |
border: 1px solid var(--border-color); | |
border-radius: 50%; | |
background: var(--bg-tertiary); | |
color: var(--text-muted); | |
cursor: pointer; | |
transition: all 0.3s ease; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 0.8rem; | |
} | |
.sw-queue-item-btn:hover { | |
border-color: var(--accent-primary); | |
background: var(--accent-primary); | |
color: white; | |
} | |
.sw-queue-drag-handle { | |
cursor: grab; | |
color: var(--text-muted); | |
margin-right: 8px; | |
} | |
.sw-queue-drag-handle:active { | |
cursor: grabbing; | |
} | |
.sw-queue-item.dragging { | |
opacity: 0.5; | |
transform: rotate(5deg); | |
} | |
.sw-queue-empty { | |
padding: 40px 20px; | |
text-align: center; | |
color: var(--text-muted); | |
} | |
.sw-queue-empty i { | |
font-size: 2rem; | |
margin-bottom: 16px; | |
display: block; | |
} | |
.sw-playlist-controls { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: 16px; | |
margin-bottom: 24px; | |
} | |
.sw-tracklist { | |
margin-top: 24px; | |
} | |
.sw-tracklist-header { | |
font-size: 1.2rem; | |
font-weight: 600; | |
margin-bottom: 16px; | |
color: var(--text-primary); | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.sw-track { | |
padding: 16px 20px; | |
margin: 8px 0; | |
border-radius: var(--border-radius); | |
background: var(--bg-tertiary); | |
border: 1px solid var(--border-color); | |
transition: all 0.3s ease; | |
cursor: pointer; | |
font-size: 0.9rem; | |
position: relative; | |
overflow: hidden; | |
} | |
.sw-track::before { | |
content: ''; | |
position: absolute; | |
left: 0; | |
top: 0; | |
width: 4px; | |
height: 100%; | |
background: var(--gradient-primary); | |
transform: scaleY(0); | |
transition: transform 0.3s ease; | |
} | |
.sw-track:hover { | |
background: var(--bg-hover); | |
transform: translateX(8px); | |
box-shadow: var(--shadow-sm); | |
} | |
.sw-track:hover::before { | |
transform: scaleY(1); | |
} | |
.sw-track.active { | |
background: var(--bg-hover); | |
border-color: var(--accent-primary); | |
color: var(--accent-primary); | |
box-shadow: var(--shadow-sm); | |
} | |
.sw-track.active::before { | |
transform: scaleY(1); | |
} | |
.sw-modal { | |
position: fixed; | |
inset: 0; | |
background: rgba(0, 0, 0, 0.8); | |
display: none; | |
align-items: center; | |
justify-content: center; | |
padding: 20px; | |
z-index: 1000; | |
backdrop-filter: blur(5px); | |
} | |
.sw-modal-content { | |
background: var(--bg-secondary); | |
width: 100%; | |
max-width: 500px; | |
border-radius: var(--border-radius-lg); | |
padding: 32px; | |
border: 1px solid var(--border-color); | |
box-shadow: var(--shadow-lg); | |
max-height: 80vh; | |
overflow-y: auto; | |
} | |
.sw-modal h2 { | |
font-size: 1.5rem; | |
font-weight: 600; | |
margin-bottom: 24px; | |
color: var(--text-primary); | |
} | |
.sw-input { | |
width: 100%; | |
padding: 16px; | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
font-size: 1rem; | |
font-family: inherit; | |
margin-bottom: 24px; | |
background: var(--bg-tertiary); | |
color: var(--text-primary); | |
transition: all 0.3s ease; | |
} | |
.sw-input:focus { | |
outline: none; | |
border-color: var(--accent-primary); | |
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.2); | |
} | |
.sw-input::placeholder { | |
color: var(--text-muted); | |
} | |
.sw-checkbox-list { | |
max-height: 300px; | |
overflow-y: auto; | |
margin-bottom: 24px; | |
border: 1px solid var(--border-color); | |
border-radius: var(--border-radius); | |
background: var(--bg-tertiary); | |
} | |
.sw-checkbox-item { | |
padding: 16px 20px; | |
border-bottom: 1px solid var(--border-color); | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
transition: background 0.3s ease; | |
} | |
.sw-checkbox-item:last-child { | |
border-bottom: none; | |
} | |
.sw-checkbox-item:hover { | |
background: var(--bg-hover); | |
} | |
.sw-checkbox-item input[type="checkbox"] { | |
width: 18px; | |
height: 18px; | |
accent-color: var(--accent-primary); | |
cursor: pointer; | |
} | |
.sw-checkbox-item label { | |
cursor: pointer; | |
font-size: 0.9rem; | |
color: var(--text-secondary); | |
flex: 1; | |
} | |
.sw-modal-actions { | |
display: flex; | |
gap: 16px; | |
justify-content: flex-end; | |
} | |
/* Touch-friendly improvements */ | |
@media (hover: none) and (pointer: coarse) { | |
.sw-btn { | |
min-height: 48px; | |
padding: 16px 20px; | |
font-size: 1rem; | |
} | |
.sw-track { | |
min-height: 56px; | |
padding: 18px 20px; | |
font-size: 1rem; | |
} | |
.sw-dropdown { | |
min-height: 48px; | |
padding: 16px; | |
font-size: 1rem; | |
} | |
.sw-input { | |
min-height: 48px; | |
padding: 16px; | |
font-size: 1rem; | |
} | |
} | |
/* Responsive Design */ | |
@media (max-width: 768px) { | |
.sw-container { | |
padding: 12px; | |
} | |
.sw-player { | |
padding: 20px; | |
margin-bottom: 16px; | |
} | |
.sw-header { | |
font-size: 2rem; | |
margin-bottom: 24px; | |
} | |
.sw-now-playing { | |
padding: 12px; | |
margin-bottom: 20px; | |
} | |
.sw-now-playing-song { | |
font-size: 1rem; | |
} | |
.sw-custom-player { | |
margin: 20px 0; | |
padding: 16px; | |
} | |
.sw-audio-controls { | |
flex-direction: column; | |
gap: 16px; | |
} | |
.sw-volume-control { | |
justify-content: center; | |
} | |
.sw-speed-control { | |
justify-content: center; | |
} | |
.sw-upload-section { | |
margin: 20px 0; | |
padding: 16px; | |
} | |
.sw-upload-area { | |
padding: 30px 15px; | |
} | |
.sw-upload-icon { | |
font-size: 2.5rem; | |
} | |
.sw-upload-text { | |
font-size: 1rem; | |
} | |
.sw-controls { | |
gap: 8px; | |
margin: 20px 0; | |
} | |
.sw-btn { | |
min-width: 70px; | |
padding: 12px 16px; | |
font-size: 0.85rem; | |
min-height: 44px; | |
} | |
.sw-btn span { | |
display: none; | |
} | |
.sw-btn i { | |
font-size: 1.1rem; | |
} | |
.sw-search-section { | |
margin-bottom: 20px; | |
} | |
.sw-search-input { | |
padding: 14px 45px 14px 14px; | |
font-size: 0.9rem; | |
} | |
.sw-filter-options { | |
gap: 6px; | |
} | |
.sw-filter-btn { | |
padding: 6px 12px; | |
font-size: 0.8rem; | |
min-height: 36px; | |
} | |
.sw-filter-btn span { | |
display: none; | |
} | |
.sw-playlist-grid { | |
grid-template-columns: 1fr; | |
gap: 12px; | |
} | |
.sw-playlist-card { | |
padding: 16px; | |
} | |
.sw-playlist-card-actions { | |
flex-wrap: wrap; | |
gap: 6px; | |
} | |
.sw-playlist-card-btn { | |
padding: 6px 12px; | |
font-size: 0.8rem; | |
} | |
.sw-queue-section { | |
margin-top: 24px; | |
} | |
.sw-queue-header { | |
padding: 12px 16px; | |
flex-direction: column; | |
gap: 12px; | |
align-items: stretch; | |
} | |
.sw-queue-title { | |
justify-content: center; | |
} | |
.sw-queue-controls { | |
justify-content: center; | |
} | |
.sw-queue-btn { | |
padding: 8px 12px; | |
font-size: 0.8rem; | |
} | |
.sw-queue-btn span { | |
display: none; | |
} | |
.sw-queue-item { | |
padding: 10px 16px; | |
} | |
.sw-queue-item-number { | |
width: 25px; | |
margin-right: 8px; | |
} | |
.sw-queue-drag-handle { | |
margin-right: 6px; | |
} | |
.sw-playlist-controls { | |
grid-template-columns: 1fr; | |
gap: 12px; | |
} | |
.sw-playlist-controls .sw-btn span { | |
display: inline; | |
} | |
.sw-dropdown { | |
padding: 14px; | |
font-size: 0.9rem; | |
margin-bottom: 20px; | |
} | |
.sw-tracklist { | |
margin-top: 20px; | |
} | |
.sw-tracklist-header { | |
font-size: 1.1rem; | |
margin-bottom: 12px; | |
} | |
.sw-track { | |
padding: 16px; | |
margin: 6px 0; | |
font-size: 0.9rem; | |
} | |
.sw-modal-content { | |
padding: 20px; | |
margin: 12px; | |
max-height: 85vh; | |
} | |
.sw-modal h2 { | |
font-size: 1.3rem; | |
margin-bottom: 20px; | |
} | |
.sw-input { | |
padding: 14px; | |
margin-bottom: 20px; | |
font-size: 0.9rem; | |
} | |
.sw-checkbox-list { | |
margin-bottom: 20px; | |
} | |
.sw-checkbox-item { | |
padding: 14px 16px; | |
} | |
.sw-modal-actions { | |
gap: 12px; | |
} | |
.sw-modal-actions .sw-btn { | |
flex: 1; | |
justify-content: center; | |
} | |
.sw-modal-actions .sw-btn span { | |
display: inline; | |
} | |
} | |
@media (max-width: 480px) { | |
.sw-container { | |
padding: 8px; | |
} | |
.sw-player { | |
padding: 16px; | |
} | |
.sw-header { | |
font-size: 1.8rem; | |
margin-bottom: 20px; | |
} | |
.sw-controls { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: 8px; | |
margin: 16px 0; | |
} | |
.sw-btn { | |
min-width: auto; | |
padding: 10px 12px; | |
font-size: 0.8rem; | |
min-height: 40px; | |
} | |
.sw-btn i { | |
font-size: 1rem; | |
} | |
.sw-playlist-controls { | |
gap: 8px; | |
} | |
.sw-track { | |
padding: 14px 12px; | |
font-size: 0.85rem; | |
} | |
.sw-modal-content { | |
padding: 16px; | |
margin: 8px; | |
} | |
.sw-modal h2 { | |
font-size: 1.2rem; | |
margin-bottom: 16px; | |
} | |
.sw-modal-actions { | |
flex-direction: column; | |
gap: 8px; | |
} | |
.sw-modal-actions .sw-btn { | |
width: 100%; | |
} | |
} | |
/* Scrollbar Styling */ | |
::-webkit-scrollbar { | |
width: 8px; | |
} | |
::-webkit-scrollbar-track { | |
background: var(--bg-tertiary); | |
border-radius: 4px; | |
} | |
::-webkit-scrollbar-thumb { | |
background: var(--border-color); | |
border-radius: 4px; | |
} | |
::-webkit-scrollbar-thumb:hover { | |
background: var(--text-muted); | |
} | |
/* Loading Animation */ | |
.sw-loading { | |
display: inline-block; | |
width: 20px; | |
height: 20px; | |
border: 2px solid var(--border-color); | |
border-radius: 50%; | |
border-top-color: var(--accent-primary); | |
animation: spin 1s ease-in-out infinite; | |
} | |
@keyframes spin { | |
to { transform: rotate(360deg); } | |
} | |
</style> | |
</head> | |
<body> | |
<div class="sw-container"> | |
<div class="sw-player"> | |
<h1 class="sw-header"> | |
<i class="fas fa-music"></i> Music Player | |
</h1> | |
<div class="sw-now-playing"> | |
<div class="sw-now-playing-label">Now Playing</div> | |
<div class="sw-now-playing-song" id="swCurrentSong">No song selected</div> | |
</div> | |
<!-- Custom Audio Player --> | |
<div class="sw-custom-player"> | |
<audio id="swAudio" preload="none" autoplay="false"> | |
Your browser does not support audio playback. | |
</audio> | |
<!-- Progress Bar --> | |
<div class="sw-progress-container"> | |
<div class="sw-time-display"> | |
<span id="swCurrentTime">0:00</span> | |
<span id="swDuration">0:00</span> | |
</div> | |
<div class="sw-progress-bar" id="swProgressBar"> | |
<div class="sw-progress-fill" id="swProgressFill"></div> | |
<div class="sw-progress-handle" id="swProgressHandle"></div> | |
</div> | |
</div> | |
<!-- Volume and Speed Controls --> | |
<div class="sw-audio-controls"> | |
<div class="sw-volume-control"> | |
<i class="fas fa-volume-up"></i> | |
<div class="sw-volume-slider"> | |
<input type="range" id="swVolumeSlider" min="0" max="100" value="70" class="sw-slider"> | |
</div> | |
<span id="swVolumeValue">70%</span> | |
</div> | |
<div class="sw-speed-control"> | |
<i class="fas fa-tachometer-alt"></i> | |
<select id="swSpeedSelect" class="sw-speed-dropdown"> | |
<option value="0.5">0.5x</option> | |
<option value="0.75">0.75x</option> | |
<option value="1" selected>1x</option> | |
<option value="1.25">1.25x</option> | |
<option value="1.5">1.5x</option> | |
<option value="2">2x</option> | |
</select> | |
</div> | |
</div> | |
</div> | |
<div class="sw-controls"> | |
<button class="sw-btn" onclick="swPrevious()"> | |
<i class="fas fa-step-backward"></i> | |
<span>Previous</span> | |
</button> | |
<button class="sw-btn" onclick="swNext()"> | |
<i class="fas fa-step-forward"></i> | |
<span>Next</span> | |
</button> | |
<button class="sw-btn" id="swLoopBtn" onclick="swToggleLoop()"> | |
<i class="fas fa-redo"></i> | |
<span>Loop</span> | |
</button> | |
<button class="sw-btn" id="swShuffleBtn" onclick="swToggleShuffle()"> | |
<i class="fas fa-random"></i> | |
<span>Shuffle</span> | |
</button> | |
</div> | |
<select class="sw-dropdown" id="swPlaylistSelect" onchange="swChangePlaylist()"> | |
<option value="all">🎵 All Songs</option> | |
</select> | |
<!-- Search and Filter Section --> | |
<div class="sw-search-section"> | |
<div class="sw-search-container"> | |
<input type="text" class="sw-search-input" id="swSearchInput" placeholder="Search songs or playlists..." oninput="swHandleSearch()"> | |
<button class="sw-search-clear" id="swSearchClear" onclick="swClearSearch()" style="display: none;"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="sw-filter-options"> | |
<button class="sw-filter-btn active" data-filter="all" onclick="swSetFilter('all')"> | |
<i class="fas fa-list"></i> All | |
</button> | |
<button class="sw-filter-btn" data-filter="songs" onclick="swSetFilter('songs')"> | |
<i class="fas fa-music"></i> Songs Only | |
</button> | |
<button class="sw-filter-btn" data-filter="playlists" onclick="swSetFilter('playlists')"> | |
<i class="fas fa-folder"></i> My Playlists | |
</button> | |
</div> | |
</div> | |
<div class="sw-playlist-controls"> | |
<button class="sw-btn" onclick="swShowPlaylistModal()"> | |
<i class="fas fa-plus"></i> | |
<span>Create Playlist</span> | |
</button> | |
<button class="sw-btn" onclick="swShowManageModal()" id="swManagePlaylistBtn" style="display: none;"> | |
<i class="fas fa-edit"></i> | |
<span>Manage Playlist</span> | |
</button> | |
<button class="sw-btn" onclick="swDeletePlaylist()"> | |
<i class="fas fa-trash"></i> | |
<span>Delete Playlist</span> | |
</button> | |
</div> | |
<!-- File Upload Section --> | |
<div class="sw-upload-section"> | |
<div class="sw-upload-header"> | |
<i class="fas fa-cloud-upload-alt"></i> | |
Add New Songs | |
</div> | |
<div class="sw-upload-area" id="swUploadArea"> | |
<div class="sw-upload-content"> | |
<i class="fas fa-cloud-upload-alt sw-upload-icon"></i> | |
<p class="sw-upload-text">Drag & drop audio files here</p> | |
<p class="sw-upload-subtext">or</p> | |
<button class="sw-btn sw-upload-btn" onclick="document.getElementById('swFileInput').click()"> | |
<i class="fas fa-folder-open"></i> | |
<span>Browse Files</span> | |
</button> | |
<input type="file" id="swFileInput" accept=".mp3,.wav,.m4a,.flac,.ogg,.aac" multiple style="display: none;"> | |
<p class="sw-upload-info">Supported formats: MP3, WAV, M4A, FLAC, OGG, AAC (Max 50MB each)</p> | |
</div> | |
</div> | |
<div class="sw-upload-progress" id="swUploadProgress" style="display: none;"> | |
<div class="sw-progress-item"> | |
<div class="sw-progress-info"> | |
<span id="swUploadFileName">Uploading...</span> | |
<span id="swUploadPercent">0%</span> | |
</div> | |
<div class="sw-progress-bar-upload"> | |
<div class="sw-progress-fill-upload" id="swUploadProgressFill"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="sw-tracklist"> | |
<div class="sw-tracklist-header"> | |
<i class="fas fa-list"></i> | |
Track List | |
</div> | |
<!-- Rendered by the JavaScript swUpdateTracks() --> | |
</div> | |
<!-- Playlist View (hidden by default) --> | |
<div class="sw-playlist-view" id="swPlaylistView" style="display: none;"> | |
<div class="sw-playlist-view-header"> | |
<i class="fas fa-folder"></i> | |
<span>My Playlists</span> | |
</div> | |
<div class="sw-playlist-grid" id="swPlaylistGrid"> | |
<!-- Playlist cards will be populated here --> | |
</div> | |
</div> | |
<!-- Queue View --> | |
<div class="sw-queue-section"> | |
<div class="sw-queue-header"> | |
<div class="sw-queue-title"> | |
<i class="fas fa-list-ol"></i> | |
<span>Play Queue</span> | |
<span class="sw-queue-count" id="swQueueCount">(0)</span> | |
</div> | |
<div class="sw-queue-controls"> | |
<button class="sw-queue-btn" id="swToggleQueueBtn" onclick="swToggleQueue()"> | |
<i class="fas fa-eye"></i> | |
<span>Show Queue</span> | |
</button> | |
<button class="sw-queue-btn" onclick="swClearQueue()"> | |
<i class="fas fa-trash"></i> | |
<span>Clear</span> | |
</button> | |
</div> | |
</div> | |
<div class="sw-queue-content" id="swQueueContent" style="display: none;"> | |
<div class="sw-queue-list" id="swQueueList"> | |
<!-- Queue items will be populated here --> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="sw-modal" id="swPlaylistModal"> | |
<div class="sw-modal-content"> | |
<h2><i class="fas fa-plus-circle"></i> Create New Playlist</h2> | |
<input type="text" class="sw-input" id="swPlaylistName" placeholder="Enter playlist name..."> | |
<div class="sw-checkbox-list" id="swSongCheckboxes"></div> | |
<div class="sw-modal-actions"> | |
<button class="sw-btn" onclick="swCloseModal()"> | |
<i class="fas fa-times"></i> | |
<span>Cancel</span> | |
</button> | |
<button class="sw-btn" onclick="swCreatePlaylist()"> | |
<i class="fas fa-save"></i> | |
<span>Save Playlist</span> | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Advanced Playlist Management Modal --> | |
<div class="sw-modal" id="swPlaylistManageModal"> | |
<div class="sw-modal-content sw-modal-large"> | |
<h2><i class="fas fa-edit"></i> Manage Playlist: <span id="swManagePlaylistName"></span></h2> | |
<!-- Playlist Name Editor --> | |
<div class="sw-playlist-editor"> | |
<label class="sw-label">Playlist Name:</label> | |
<div class="sw-name-editor"> | |
<input type="text" class="sw-input" id="swEditPlaylistName" placeholder="Enter new playlist name..."> | |
<button class="sw-btn sw-btn-small" onclick="swRenamePlaylist()"> | |
<i class="fas fa-check"></i> | |
<span>Rename</span> | |
</button> | |
</div> | |
</div> | |
<!-- Add Songs Section --> | |
<div class="sw-manage-section"> | |
<h3><i class="fas fa-plus"></i> Add Songs</h3> | |
<div class="sw-available-songs" id="swAvailableSongs"> | |
<!-- Available songs will be populated here --> | |
</div> | |
</div> | |
<!-- Current Playlist Songs --> | |
<div class="sw-manage-section"> | |
<h3><i class="fas fa-list"></i> Current Songs <span class="sw-song-count" id="swCurrentSongCount">(0)</span></h3> | |
<div class="sw-current-songs" id="swCurrentSongs"> | |
<!-- Current playlist songs will be populated here --> | |
</div> | |
</div> | |
<div class="sw-modal-actions"> | |
<button class="sw-btn" onclick="swCloseManageModal()"> | |
<i class="fas fa-times"></i> | |
<span>Close</span> | |
</button> | |
</div> | |
</div> | |
</div> | |
<script> | |
</script> | |
</body> | |
</html> | |