NeoPy commited on
Commit
aeb45e6
·
verified ·
1 Parent(s): 7cea91d

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +595 -276
app.js CHANGED
@@ -1,348 +1,667 @@
1
  // ==UserScript==
2
- // @name Spotify Dynamic UI/UX Animations
3
  // @namespace https://violentmonkey.github.io/
4
- // @version 1.1
5
- // @description Enhances Spotify web player with stylish dynamic animations and smooth transitions for a premium UI/UX experience
6
- // @author UI/UX Enhancement Team
7
  // @match https://open.spotify.com/*
8
- // @icon https://open.spotify.com/favicon.ico
9
  // @grant GM_addStyle
10
- // @grant GM_setValue
11
  // @grant GM_getValue
 
12
  // @run-at document-start
13
- // @updateURL https://github.com/user/spotify-ui-enhancer/raw/main/spotify-dynamic-ui.user.js
14
- // @downloadURL https://github.com/user/spotify-ui-enhancer/raw/main/spotify-dynamic-ui.user.js
15
  // ==/UserScript==
16
 
17
  (function() {
18
  'use strict';
19
 
20
- // Check if we're on Spotify web player
21
- if (!window.location.hostname.includes('spotify.com')) {
22
- return;
 
 
23
  }
24
 
25
- // Custom CSS animations and transitions optimized for Violentmonkey
26
- GM_addStyle(`
27
- /* Global smooth transitions */
28
- :root {
29
- --transition-fast: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
30
- --transition-normal: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
31
- --transition-slow: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
32
- --shadow-normal: 0 4px 12px rgba(0, 0, 0, 0.15);
33
- --shadow-hover: 0 8px 25px rgba(0, 0, 0, 0.25);
34
- --shadow-glow: 0 0 15px rgba(0, 255, 150, 0.3);
35
- }
 
 
36
 
37
- /* Enhanced buttons with hover effects */
38
- button, .Button-sc-1dqy6sn-0, .ButtonInner-sc-1dqy6sn-1 {
39
- transition: var(--transition-normal) !important;
40
- transform: scale(1) !important;
41
- box-shadow: var(--shadow-normal) !important;
42
- will-change: transform, box-shadow;
43
- }
 
 
 
44
 
45
- button:hover, .Button-sc-1dqy6sn-0:hover, .ButtonInner-sc-1dqy6sn-1:hover {
46
- transform: scale(1.05) !important;
47
- box-shadow: var(--shadow-hover) !important;
48
- filter: brightness(1.1) !important;
49
- }
50
 
51
- /* Floating play button pulse animation */
52
- .Button-sc-1dqy6sn-0.Lsg1a0PdZMXQ3vQZlE0_M {
53
- animation: pulse 2s infinite !important;
54
- position: relative !important;
55
- z-index: 10 !important;
56
- }
 
 
57
 
58
- /* Card hover effects with lift animation */
59
- .Card-sc-1uyc430-0, .main-card-card, .main-shelf-shelf {
60
- transition: var(--transition-normal) !important;
61
- transform: translateY(0) !important;
62
- box-shadow: var(--shadow-normal) !important;
63
- will-change: transform, box-shadow;
64
- }
65
 
66
- .Card-sc-1uyc430-0:hover, .main-card-card:hover, .main-shelf-shelf:hover {
67
- transform: translateY(-5px) !important;
68
- box-shadow: var(--shadow-hover) !important;
69
- z-index: 100 !important;
70
- }
 
71
 
72
- /* Album art hover effects */
73
- .cover-art-image, .main-image-image {
74
- transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275),
75
- box-shadow 0.4s ease !important;
76
- transform-origin: center !important;
77
- will-change: transform, box-shadow;
78
- }
79
 
80
- .cover-art-image:hover, .main-image-image:hover {
81
- transform: scale(1.1) rotate(2deg) !important;
82
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4), var(--shadow-glow) !important;
83
- z-index: 1000 !important;
84
- }
 
 
85
 
86
- /* Progress bar enhancements */
87
- .progress-bar__slider, .playback-progressbar-slider {
88
- transition: all 0.2s ease !important;
89
- will-change: transform, box-shadow;
90
- }
91
 
92
- .progress-bar__slider:hover, .playback-progressbar-slider:hover {
93
- transform: scale(1.3) !important;
94
- box-shadow: 0 0 0 3px rgba(0, 255, 150, 0.7) !important;
95
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- /* Volume control slider */
98
- .volume-bar__slider, .playback-volumebar-slider {
99
- transition: transform 0.2s ease, box-shadow 0.2s ease !important;
100
- will-change: transform, box-shadow;
101
- }
102
 
103
- .volume-bar__slider:hover, .playback-volumebar-slider:hover {
104
- transform: scale(1.2) !important;
105
- box-shadow: 0 0 8px rgba(0, 255, 150, 0.7) !important;
106
- }
 
 
107
 
108
- /* Text animations */
109
- .Type__TypeElement-sc-goli3j-0, .main-type-type {
110
- transition: opacity 0.3s ease, transform 0.3s ease !important;
111
- will-change: opacity, transform;
112
- }
 
 
 
 
113
 
114
- .Type__TypeElement-sc-goli3j-0:hover, .main-type-type:hover {
115
- opacity: 1 !important;
116
- transform: translateY(0) !important;
117
- }
 
 
 
 
 
 
 
 
 
 
118
 
119
- /* Now playing bar animations */
120
- .now-playing-bar, .playback-bar {
121
- transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55),
122
- box-shadow 0.3s ease !important;
123
- will-change: transform, box-shadow;
124
- }
125
 
126
- .now-playing-bar:hover, .playback-bar:hover {
127
- transform: translateY(-3px) !important;
128
- box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.2) !important;
129
- }
 
 
 
 
130
 
131
- /* Menu animations */
132
- .menu-container, .main-contextMenu-menu {
133
- opacity: 0 !important;
134
- transform: translateY(10px) !important;
135
- transition: opacity 0.2s ease, transform 0.2s ease !important;
136
- }
 
 
137
 
138
- .menu-container.show, .main-contextMenu-menu.show {
139
- opacity: 1 !important;
140
- transform: translateY(0) !important;
141
- }
142
 
143
- /* Keyframes for animations */
144
- @keyframes pulse {
145
- 0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 255, 150, 0.4); }
146
- 70% { transform: scale(1.05); box-shadow: 0 0 0 10px rgba(0, 255, 150, 0); }
147
- 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 255, 150, 0); }
148
- }
149
 
150
- @keyframes fadeInUp {
151
- from {
152
- opacity: 0;
153
- transform: translateY(20px);
 
154
  }
155
- to {
156
- opacity: 1;
157
- transform: translateY(0);
 
158
  }
159
- }
160
 
161
- @keyframes shimmer {
162
- 0% { background-position: -1000px 0; }
163
- 100% { background-position: 1000px 0; }
164
- }
165
 
166
- /* Loading animations */
167
- .loading-shimmer, .main-loading-loading {
168
- background: linear-gradient(90deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.1) 100%);
169
- background-size: 1000px 100%;
170
- animation: shimmer 2s infinite linear !important;
171
- }
 
172
 
173
- /* Sidebar transitions */
174
- .sidebar, .main-buddyFeed-buddyFeed {
175
- transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1) !important;
176
- }
 
 
177
 
178
- .sidebar:hover, .main-buddyFeed-buddyFeed:hover {
179
- transform: translateX(5px) !important;
180
- }
 
 
181
 
182
- /* Enhanced input fields */
183
- input, textarea, .main-inputInput {
184
- transition: box-shadow 0.3s ease, transform 0.2s ease !important;
185
- }
186
 
187
- input:focus, textarea:focus, .main-inputInput:focus {
188
- box-shadow: 0 0 0 3px rgba(0, 255, 150, 0.5) !important;
189
- transform: scale(1.01) !important;
190
- }
 
 
 
 
 
 
 
 
191
 
192
- /* Performance optimization */
193
- * {
194
- will-change: auto !important;
195
- }
196
- `);
197
-
198
- // JavaScript animations and interactions
199
- function enhanceAnimations() {
200
- // Enhanced hover effects for cards with proper cleanup
201
- document.querySelectorAll('.Card-sc-1uyc430-0, .main-card-card, .main-shelf-shelf').forEach(card => {
202
- card.addEventListener('mouseenter', () => {
203
- card.style.zIndex = '100';
204
- card.style.transition = 'all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)';
205
- }, { passive: true });
206
-
207
- card.addEventListener('mouseleave', () => {
208
- card.style.zIndex = '1';
209
- }, { passive: true });
210
- });
211
 
212
- // Dynamic album art effects
213
- document.querySelectorAll('.cover-art-image, .main-image-image').forEach(cover => {
214
- cover.addEventListener('mouseenter', () => {
215
- cover.style.zIndex = '1000';
216
- cover.style.transition = 'transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.4s ease';
217
- }, { passive: true });
218
-
219
- cover.addEventListener('mouseleave', () => {
220
- cover.style.zIndex = '1';
221
- }, { passive: true });
222
- });
 
223
 
224
- // Enhanced progress bar interaction
225
- const progressBars = document.querySelectorAll('.progress-bar, .playback-progressbar');
226
- progressBars.forEach(bar => {
227
- bar.addEventListener('click', (e) => {
228
- const rect = bar.getBoundingClientRect();
229
- const percent = (e.clientX - rect.left) / rect.width;
230
- animateProgressBar(percent);
231
- }, { passive: true });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  });
 
233
 
234
- // Animation for new elements added to DOM
235
- const observer = new MutationObserver((mutations) => {
 
236
  mutations.forEach(mutation => {
237
  if (mutation.addedNodes.length) {
238
- applyEnterAnimations(mutation.addedNodes);
 
 
 
 
239
  }
240
  });
241
  });
242
 
243
- // Observe the entire document body for changes
244
  observer.observe(document.body, {
245
  childList: true,
246
  subtree: true
247
  });
248
 
249
- // Cleanup function
250
- window.addEventListener('beforeunload', () => {
251
- observer.disconnect();
252
- }, { passive: true });
 
 
 
 
 
253
  }
254
 
255
- function animateProgressBar(percent) {
256
- const sliders = document.querySelectorAll('.progress-bar__slider, .playback-progressbar-slider');
257
- sliders.forEach(slider => {
258
- if (slider) {
259
- slider.style.transform = 'scale(1.5)';
260
- setTimeout(() => {
261
- slider.style.transform = 'scale(1)';
262
- }, 300);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  }
264
- });
265
- }
266
 
267
- function applyEnterAnimations(nodes) {
268
- Array.from(nodes).forEach(node => {
269
- if (node.nodeType === 1) {
270
- // Check for card-like elements
271
- const isCard = node.classList.contains('Card-sc-1uyc430-0') ||
272
- node.classList.contains('main-card-card') ||
273
- node.classList.contains('main-shelf-shelf');
 
 
 
 
 
274
 
275
- if (isCard) {
276
- node.style.animation = 'fadeInUp 0.4s ease forwards';
277
- node.style.opacity = '0';
278
- node.style.transform = 'translateY(20px)';
279
- setTimeout(() => {
280
- node.style.opacity = '1';
281
- node.style.transform = 'translateY(0)';
282
- }, 10);
 
 
 
 
 
 
283
  }
284
  }
285
- });
 
 
 
 
 
 
 
286
  }
287
 
288
- // Initialize animations when DOM is ready
289
- if (document.readyState === 'loading') {
290
- document.addEventListener('DOMContentLoaded', enhanceAnimations, { passive: true });
291
- } else {
292
- enhanceAnimations();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  }
294
 
295
- // Periodic animation refresh for dynamic content
296
- const animationInterval = setInterval(() => {
297
- if (document.body) {
298
- enhanceAnimations();
 
 
 
 
 
 
 
 
 
299
  }
300
- }, 2000);
301
-
302
- // Cleanup on page unload
303
- window.addEventListener('beforeunload', () => {
304
- clearInterval(animationInterval);
305
- }, { passive: true });
306
-
307
- console.log('✅ Spotify Dynamic UI/UX Animations loaded successfully via Violentmonkey!');
308
-
309
- // Optional: Add a small indicator that the script is active
310
- setTimeout(() => {
311
- if (document.querySelector('.now-playing-bar') || document.querySelector('.playback-bar')) {
312
- const style = document.createElement('style');
313
- style.textContent = `
314
- .vm-spotify-indicator {
315
- position: fixed;
316
- bottom: 20px;
317
- right: 20px;
318
- background: rgba(0, 255, 150, 0.8);
319
- color: black;
320
- padding: 4px 8px;
321
- border-radius: 12px;
322
- font-size: 12px;
323
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
324
- z-index: 9999;
325
- animation: fadeIn 0.3s ease forwards;
326
- }
327
- @keyframes fadeIn {
328
- from { opacity: 0; transform: scale(0.8); }
329
- to { opacity: 1; transform: scale(1); }
330
- }
331
- `;
332
- document.head.appendChild(style);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
 
334
- const indicator = document.createElement('div');
335
- indicator.className = 'vm-spotify-indicator';
336
- indicator.textContent = '✨ Enhanced UI Active';
337
- document.body.appendChild(indicator);
338
 
339
- setTimeout(() => {
340
- indicator.style.animation = 'fadeOut 0.3s ease forwards';
341
- setTimeout(() => {
342
- indicator.remove();
343
- document.head.removeChild(style);
344
- }, 300);
345
- }, 3000);
346
  }
347
- }, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  })();
 
1
  // ==UserScript==
2
+ // @name Spotify Enhanced UI/UX with Audio Visualizer
3
  // @namespace https://violentmonkey.github.io/
4
+ // @version 2.1
5
+ // @description Transforms Spotify web player with dynamic animations, stylish UI enhancements, and a mesmerizing audio visualizer
6
+ // @author UI/UX Designer
7
  // @match https://open.spotify.com/*
8
+ // @icon https://www.spotify.com/favicon.ico
9
  // @grant GM_addStyle
 
10
  // @grant GM_getValue
11
+ // @grant GM_setValue
12
  // @run-at document-start
13
+ // @updateURL https://github.com/user/spotify-enhanced/raw/main/script.user.js
14
+ // @downloadURL https://github.com/user/spotify-enhanced/raw/main/script.user.js
15
  // ==/UserScript==
16
 
17
  (function() {
18
  'use strict';
19
 
20
+ // Wait for DOM to be fully loaded
21
+ if (document.readyState !== 'complete') {
22
+ window.addEventListener('load', initEnhancedSpotify);
23
+ } else {
24
+ initEnhancedSpotify();
25
  }
26
 
27
+ function initEnhancedSpotify() {
28
+ // Create style element for custom CSS
29
+ const style = document.createElement('style');
30
+ style.textContent = `
31
+ /* Global UI Enhancements */
32
+ :root {
33
+ --primary-gradient: linear-gradient(45deg, #1DB954, #1ED760, #00ffcc);
34
+ --secondary-gradient: linear-gradient(45deg, #8A2BE2, #9370DB, #BA55D3);
35
+ --glow-color: rgba(0, 255, 204, 0.7);
36
+ --transition-fast: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
37
+ --transition-normal: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
38
+ --transition-slow: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
39
+ }
40
 
41
+ /* Enhanced buttons */
42
+ button, .Button-sc-1dqy6sn-0, .ButtonInner-sc-1dqy6sn-1 {
43
+ transition: var(--transition-normal) !important;
44
+ transform: scale(1) !important;
45
+ background: var(--primary-gradient) !important;
46
+ background-size: 200% 200% !important;
47
+ animation: gradientBG 3s ease infinite !important;
48
+ box-shadow: 0 4px 15px rgba(0, 255, 204, 0.3) !important;
49
+ border: none !important;
50
+ }
51
 
52
+ button:hover, .Button-sc-1dqy6sn-0:hover, .ButtonInner-sc-1dqy6sn-1:hover {
53
+ transform: scale(1.08) !important;
54
+ box-shadow: 0 6px 25px rgba(0, 255, 204, 0.5) !important;
55
+ filter: brightness(1.1) !important;
56
+ }
57
 
58
+ /* Card hover effects */
59
+ .Card-sc-1uyc430-0, .main-card-card {
60
+ transition: var(--transition-normal) !important;
61
+ transform: translateY(0) !important;
62
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2) !important;
63
+ border-radius: 12px !important;
64
+ overflow: hidden !important;
65
+ }
66
 
67
+ .Card-sc-1uyc430-0:hover, .main-card-card:hover {
68
+ transform: translateY(-8px) !important;
69
+ box-shadow: 0 12px 30px rgba(0, 255, 204, 0.4) !important;
70
+ z-index: 100 !important;
71
+ }
 
 
72
 
73
+ /* Album art effects */
74
+ .cover-art-image {
75
+ transition: transform 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275),
76
+ box-shadow 0.4s ease !important;
77
+ transform-origin: center !important;
78
+ }
79
 
80
+ .cover-art-image:hover {
81
+ transform: scale(1.15) rotate(3deg) !important;
82
+ box-shadow: 0 0 30px rgba(0, 255, 204, 0.6) !important;
83
+ z-index: 1000 !important;
84
+ }
 
 
85
 
86
+ /* Now playing bar enhancements */
87
+ .now-playing-bar {
88
+ transition: var(--transition-slow) !important;
89
+ background: rgba(0, 0, 0, 0.8) !important;
90
+ backdrop-filter: blur(10px) !important;
91
+ border-top: 1px solid rgba(0, 255, 204, 0.3) !important;
92
+ }
93
 
94
+ .now-playing-bar:hover {
95
+ transform: translateY(-5px) !important;
96
+ box-shadow: 0 -10px 30px rgba(0, 255, 204, 0.3) !important;
97
+ }
 
98
 
99
+ /* Visualizer container */
100
+ #visualizer-container {
101
+ position: fixed;
102
+ bottom: 0;
103
+ left: 0;
104
+ right: 0;
105
+ height: 80px;
106
+ background: rgba(0, 0, 0, 0.7);
107
+ backdrop-filter: blur(5px);
108
+ z-index: 9999;
109
+ display: flex;
110
+ justify-content: center;
111
+ align-items: center;
112
+ padding: 0 20px;
113
+ border-top: 1px solid rgba(0, 255, 204, 0.3);
114
+ opacity: 0.9;
115
+ transition: opacity 0.3s ease;
116
+ }
117
 
118
+ #visualizer-container:hover {
119
+ opacity: 1;
120
+ }
 
 
121
 
122
+ /* Visualizer canvas */
123
+ #audio-visualizer {
124
+ width: 100%;
125
+ height: 60px;
126
+ display: block;
127
+ }
128
 
129
+ /* Visualizer control buttons */
130
+ .visualizer-controls {
131
+ position: absolute;
132
+ bottom: 5px;
133
+ right: 10px;
134
+ display: flex;
135
+ gap: 8px;
136
+ z-index: 10000;
137
+ }
138
 
139
+ .visualizer-btn {
140
+ width: 28px;
141
+ height: 28px;
142
+ border-radius: 50%;
143
+ background: rgba(255, 255, 255, 0.1);
144
+ border: 1px solid rgba(0, 255, 204, 0.5);
145
+ color: white;
146
+ display: flex;
147
+ justify-content: center;
148
+ align-items: center;
149
+ cursor: pointer;
150
+ font-size: 12px;
151
+ transition: all 0.2s ease;
152
+ }
153
 
154
+ .visualizer-btn:hover {
155
+ background: rgba(0, 255, 204, 0.3);
156
+ transform: scale(1.1);
157
+ }
 
 
158
 
159
+ /* Progress bar enhancements */
160
+ .progress-bar {
161
+ height: 4px !important;
162
+ background: rgba(255, 255, 255, 0.2) !important;
163
+ border-radius: 2px !important;
164
+ overflow: hidden !important;
165
+ position: relative !important;
166
+ }
167
 
168
+ .progress-bar__slider {
169
+ width: 14px !important;
170
+ height: 14px !important;
171
+ background: var(--primary-gradient) !important;
172
+ border: 2px solid white !important;
173
+ box-shadow: 0 0 10px var(--glow-color) !important;
174
+ transition: all 0.2s ease !important;
175
+ }
176
 
177
+ .progress-bar__slider:hover {
178
+ transform: scale(1.5) !important;
179
+ box-shadow: 0 0 20px var(--glow-color) !important;
180
+ }
181
 
182
+ /* Volume slider */
183
+ .volume-bar {
184
+ height: 4px !important;
185
+ background: rgba(255, 255, 255, 0.2) !important;
186
+ border-radius: 2px !important;
187
+ }
188
 
189
+ .volume-bar__slider {
190
+ width: 12px !important;
191
+ height: 12px !important;
192
+ background: var(--primary-gradient) !important;
193
+ box-shadow: 0 0 8px var(--glow-color) !important;
194
  }
195
+
196
+ /* Text animations */
197
+ .Type__TypeElement-sc-goli3j-0 {
198
+ transition: opacity 0.3s ease, transform 0.3s ease !important;
199
  }
 
200
 
201
+ .Type__TypeElement-sc-goli3j-0:hover {
202
+ opacity: 1 !important;
203
+ transform: translateY(-2px) !important;
204
+ }
205
 
206
+ /* Gradient text */
207
+ .glow-text {
208
+ background: var(--primary-gradient);
209
+ -webkit-background-clip: text;
210
+ -webkit-text-fill-color: transparent;
211
+ text-shadow: 0 0 10px rgba(0, 255, 204, 0.3);
212
+ }
213
 
214
+ /* Keyframes */
215
+ @keyframes gradientBG {
216
+ 0% { background-position: 0% 50% }
217
+ 50% { background-position: 100% 50% }
218
+ 100% { background-position: 0% 50% }
219
+ }
220
 
221
+ @keyframes pulse {
222
+ 0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 255, 204, 0.4); }
223
+ 70% { transform: scale(1.02); box-shadow: 0 0 0 12px rgba(0, 255, 204, 0); }
224
+ 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0, 255, 204, 0); }
225
+ }
226
 
227
+ @keyframes fadeIn {
228
+ from { opacity: 0; transform: translateY(10px); }
229
+ to { opacity: 1; transform: translateY(0); }
230
+ }
231
 
232
+ /* Visualizer presets */
233
+ .visualizer-preset-selector {
234
+ position: absolute;
235
+ bottom: 5px;
236
+ left: 10px;
237
+ background: rgba(0, 0, 0, 0.7);
238
+ border: 1px solid rgba(0, 255, 204, 0.3);
239
+ border-radius: 20px;
240
+ padding: 2px;
241
+ display: flex;
242
+ gap: 2px;
243
+ }
244
 
245
+ .preset-btn {
246
+ width: 20px;
247
+ height: 20px;
248
+ border-radius: 50%;
249
+ cursor: pointer;
250
+ transition: all 0.2s ease;
251
+ border: 2px solid transparent;
252
+ }
 
 
 
 
 
 
 
 
 
 
 
253
 
254
+ .preset-btn.active {
255
+ border-color: white;
256
+ transform: scale(1.1);
257
+ }
258
+ `;
259
+ document.head.appendChild(style);
260
+
261
+ // Create visualizer container
262
+ createVisualizerContainer();
263
+
264
+ // Add UI enhancements
265
+ enhanceUIElements();
266
 
267
+ // Setup audio analysis
268
+ setupAudioAnalysis();
269
+
270
+ console.log(' Spotify Enhanced UI/UX with Audio Visualizer loaded successfully!');
271
+ }
272
+
273
+ function createVisualizerContainer() {
274
+ const container = document.createElement('div');
275
+ container.id = 'visualizer-container';
276
+ container.innerHTML = `
277
+ <canvas id="audio-visualizer"></canvas>
278
+ <div class="visualizer-controls">
279
+ <div class="visualizer-btn" id="visualizer-toggle">❚❚</div>
280
+ <div class="visualizer-btn" id="visualizer-fullscreen">⛶</div>
281
+ </div>
282
+ <div class="visualizer-preset-selector">
283
+ <div class="preset-btn active" style="background: #1DB954;" data-preset="bars"></div>
284
+ <div class="preset-btn" style="background: #8A2BE2;" data-preset="wave"></div>
285
+ <div class="preset-btn" style="background: #FF4500;" data-preset="particles"></div>
286
+ <div class="preset-btn" style="background: #1E90FF;" data-preset="circular"></div>
287
+ </div>
288
+ `;
289
+ document.body.appendChild(container);
290
+
291
+ // Setup event listeners
292
+ document.getElementById('visualizer-toggle').addEventListener('click', toggleVisualizer);
293
+ document.getElementById('visualizer-fullscreen').addEventListener('click', toggleFullscreenVisualizer);
294
+
295
+ document.querySelectorAll('.preset-btn').forEach(btn => {
296
+ btn.addEventListener('click', function() {
297
+ document.querySelectorAll('.preset-btn').forEach(b => b.classList.remove('active'));
298
+ this.classList.add('active');
299
+ setCurrentPreset(this.dataset.preset);
300
+ });
301
  });
302
+ }
303
 
304
+ function setupAudioAnalysis() {
305
+ // Get the audio element (Spotify uses audio elements dynamically)
306
+ const observer = new MutationObserver(mutations => {
307
  mutations.forEach(mutation => {
308
  if (mutation.addedNodes.length) {
309
+ const audioElements = document.querySelectorAll('audio');
310
+ if (audioElements.length > 0) {
311
+ initializeVisualizer(audioElements[0]);
312
+ observer.disconnect();
313
+ }
314
  }
315
  });
316
  });
317
 
 
318
  observer.observe(document.body, {
319
  childList: true,
320
  subtree: true
321
  });
322
 
323
+ // Fallback: check periodically if audio element appears
324
+ let audioCheckInterval = setInterval(() => {
325
+ const audioElements = document.querySelectorAll('audio');
326
+ if (audioElements.length > 0) {
327
+ initializeVisualizer(audioElements[0]);
328
+ clearInterval(audioCheckInterval);
329
+ observer.disconnect();
330
+ }
331
+ }, 1000);
332
  }
333
 
334
+ function initializeVisualizer(audioElement) {
335
+ const canvas = document.getElementById('audio-visualizer');
336
+ const ctx = canvas.getContext('2d');
337
+ const container = document.getElementById('visualizer-container');
338
+
339
+ // Set canvas dimensions
340
+ function resizeCanvas() {
341
+ canvas.width = container.clientWidth - 40;
342
+ canvas.height = container.clientHeight - 20;
343
+ }
344
+
345
+ resizeCanvas();
346
+ window.addEventListener('resize', resizeCanvas);
347
+
348
+ // Create audio context and analyzer
349
+ let audioContext, analyzer, source;
350
+
351
+ try {
352
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
353
+ analyzer = audioContext.createAnalyser();
354
+ analyzer.fftSize = 256;
355
+ analyzer.smoothingTimeConstant = 0.8;
356
+
357
+ // Create source from audio element
358
+ source = audioContext.createMediaElementSource(audioElement);
359
+ source.connect(analyzer);
360
+ analyzer.connect(audioContext.destination);
361
+
362
+ // Create buffer for frequency data
363
+ const bufferLength = analyzer.frequencyBinCount;
364
+ const dataArray = new Uint8Array(bufferLength);
365
+
366
+ // Visualizer settings
367
+ let currentPreset = 'bars';
368
+ let isPlaying = true;
369
+ let fullscreenMode = false;
370
+
371
+ function setCurrentPreset(preset) {
372
+ currentPreset = preset;
373
+ }
374
+
375
+ function toggleVisualizer() {
376
+ isPlaying = !isPlaying;
377
+ document.getElementById('visualizer-toggle').textContent = isPlaying ? '❚❚' : '▶️';
378
+ }
379
+
380
+ function toggleFullscreenVisualizer() {
381
+ fullscreenMode = !fullscreenMode;
382
+ document.getElementById('visualizer-fullscreen').textContent = fullscreenMode ? '❐' : '⛶';
383
+ container.style.height = fullscreenMode ? '300px' : '80px';
384
+ container.style.opacity = fullscreenMode ? '0.95' : '0.9';
385
+ resizeCanvas();
386
  }
 
 
387
 
388
+ // Animation function
389
+ function animate() {
390
+ if (!isPlaying) {
391
+ requestAnimationFrame(animate);
392
+ return;
393
+ }
394
+
395
+ requestAnimationFrame(animate);
396
+
397
+ analyzer.getByteFrequencyData(dataArray);
398
+
399
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
400
 
401
+ // Draw based on current preset
402
+ switch(currentPreset) {
403
+ case 'bars':
404
+ drawBars(ctx, dataArray, canvas.width, canvas.height);
405
+ break;
406
+ case 'wave':
407
+ drawWave(ctx, dataArray, canvas.width, canvas.height);
408
+ break;
409
+ case 'particles':
410
+ drawParticles(ctx, dataArray, canvas.width, canvas.height);
411
+ break;
412
+ case 'circular':
413
+ drawCircular(ctx, dataArray, canvas.width, canvas.height);
414
+ break;
415
  }
416
  }
417
+
418
+ animate();
419
+
420
+ } catch (error) {
421
+ console.error('Error initializing audio visualizer:', error);
422
+ // Fallback visualizer if audio analysis fails
423
+ createFallbackVisualizer(ctx, canvas.width, canvas.height);
424
+ }
425
  }
426
 
427
+ function drawBars(ctx, dataArray, width, height) {
428
+ const barWidth = (width / dataArray.length) * 2.5;
429
+ let x = 0;
430
+
431
+ for (let i = 0; i < dataArray.length; i++) {
432
+ const barHeight = (dataArray[i] / 255) * height * 1.2;
433
+
434
+ // Gradient for bars
435
+ const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight);
436
+ gradient.addColorStop(0, 'rgba(0, 255, 204, 0.3)');
437
+ gradient.addColorStop(1, 'rgba(0, 255, 204, 0.8)');
438
+
439
+ ctx.fillStyle = gradient;
440
+ ctx.fillRect(x, height - barHeight, barWidth, barHeight);
441
+
442
+ // Glow effect
443
+ ctx.shadowColor = 'rgba(0, 255, 204, 0.7)';
444
+ ctx.shadowBlur = 15;
445
+
446
+ x += barWidth + 1;
447
+ }
448
+
449
+ ctx.shadowBlur = 0;
450
  }
451
 
452
+ function drawWave(ctx, dataArray, width, height) {
453
+ ctx.beginPath();
454
+ ctx.moveTo(0, height / 2);
455
+
456
+ for (let i = 0; i < dataArray.length; i++) {
457
+ const x = (i / dataArray.length) * width;
458
+ const y = height / 2 + ((dataArray[i] - 128) / 128) * (height / 3);
459
+
460
+ if (i === 0) {
461
+ ctx.moveTo(x, y);
462
+ } else {
463
+ ctx.lineTo(x, y);
464
+ }
465
  }
466
+
467
+ // Gradient stroke
468
+ const gradient = ctx.createLinearGradient(0, 0, width, 0);
469
+ gradient.addColorStop(0, '#1DB954');
470
+ gradient.addColorStop(0.5, '#00ffcc');
471
+ gradient.addColorStop(1, '#1ED760');
472
+
473
+ ctx.strokeStyle = gradient;
474
+ ctx.lineWidth = 3;
475
+ ctx.lineCap = 'round';
476
+ ctx.lineJoin = 'round';
477
+ ctx.stroke();
478
+
479
+ // Fill under wave
480
+ ctx.lineTo(width, height);
481
+ ctx.lineTo(0, height);
482
+ ctx.closePath();
483
+
484
+ const fillGradient = ctx.createLinearGradient(0, 0, 0, height);
485
+ fillGradient.addColorStop(0, 'rgba(0, 255, 204, 0.3)');
486
+ fillGradient.addColorStop(1, 'rgba(0, 255, 204, 0.1)');
487
+
488
+ ctx.fillStyle = fillGradient;
489
+ ctx.fill();
490
+ }
491
+
492
+ function drawParticles(ctx, dataArray, width, height) {
493
+ const centerX = width / 2;
494
+ const centerY = height / 2;
495
+ const radius = Math.min(width, height) * 0.4;
496
+
497
+ for (let i = 0; i < dataArray.length; i += 4) {
498
+ const angle = (i / dataArray.length) * Math.PI * 2;
499
+ const distance = (dataArray[i] / 255) * radius;
500
+ const x = centerX + Math.cos(angle) * distance;
501
+ const y = centerY + Math.sin(angle) * distance;
502
+ const size = (dataArray[i] / 255) * 8 + 2;
503
+
504
+ // Particle color based on frequency
505
+ const hue = (i / dataArray.length) * 360;
506
+ ctx.fillStyle = `hsla(${hue}, 80%, 60%, ${dataArray[i] / 255})`;
507
+
508
+ ctx.beginPath();
509
+ ctx.arc(x, y, size, 0, Math.PI * 2);
510
+ ctx.fill();
511
+ }
512
+
513
+ // Center glow
514
+ const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius * 0.3);
515
+ gradient.addColorStop(0, 'rgba(0, 255, 204, 0.8)');
516
+ gradient.addColorStop(1, 'rgba(0, 255, 204, 0)');
517
+
518
+ ctx.fillStyle = gradient;
519
+ ctx.beginPath();
520
+ ctx.arc(centerX, centerY, radius * 0.3, 0, Math.PI * 2);
521
+ ctx.fill();
522
+ }
523
+
524
+ function drawCircular(ctx, dataArray, width, height) {
525
+ const centerX = width / 2;
526
+ const centerY = height / 2;
527
+ const maxRadius = Math.min(width, height) * 0.4;
528
+
529
+ ctx.save();
530
+
531
+ // Draw multiple rings
532
+ for (let ring = 0; ring < 3; ring++) {
533
+ const ringRadius = maxRadius * (1 - ring * 0.3);
534
+
535
+ ctx.beginPath();
536
+ ctx.arc(centerX, centerY, ringRadius, 0, Math.PI * 2);
537
+
538
+ const gradient = ctx.createLinearGradient(0, 0, width, 0);
539
+ gradient.addColorStop(0, '#1DB954');
540
+ gradient.addColorStop(0.5, '#00ffcc');
541
+ gradient.addColorStop(1, '#1ED760');
542
+
543
+ ctx.strokeStyle = gradient;
544
+ ctx.lineWidth = 3 + ring;
545
+ ctx.stroke();
546
+ }
547
+
548
+ // Draw frequency bars around circle
549
+ const barCount = 64;
550
+ const barWidth = (Math.PI * 2) / barCount;
551
+
552
+ for (let i = 0; i < barCount; i++) {
553
+ const angle = (i / barCount) * Math.PI * 2;
554
+ const barHeight = (dataArray[i % dataArray.length] / 255) * maxRadius * 0.3;
555
+
556
+ ctx.save();
557
+ ctx.translate(centerX, centerY);
558
+ ctx.rotate(angle);
559
+
560
+ const gradient = ctx.createLinearGradient(0, 0, 0, -barHeight);
561
+ gradient.addColorStop(0, 'rgba(0, 255, 204, 0.3)');
562
+ gradient.addColorStop(1, 'rgba(0, 255, 204, 0.8)');
563
 
564
+ ctx.fillStyle = gradient;
565
+ ctx.fillRect(0, -barHeight, 3, barHeight);
 
 
566
 
567
+ ctx.restore();
 
 
 
 
 
 
568
  }
569
+
570
+ ctx.restore();
571
+ }
572
+
573
+ function createFallbackVisualizer(ctx, width, height) {
574
+ // Create animated gradient background if audio analysis fails
575
+ let phase = 0;
576
+
577
+ function animateFallback() {
578
+ requestAnimationFrame(animateFallback);
579
+
580
+ phase += 0.01;
581
+
582
+ // Clear canvas
583
+ ctx.clearRect(0, 0, width, height);
584
+
585
+ // Create gradient
586
+ const gradient = ctx.createLinearGradient(0, 0, width, height);
587
+ gradient.addColorStop(0, `hsl(${(phase * 60) % 360}, 80%, 50%)`);
588
+ gradient.addColorStop(0.5, `hsl(${(phase * 90 + 120) % 360}, 80%, 60%)`);
589
+ gradient.addColorStop(1, `hsl(${(phase * 120 + 240) % 360}, 80%, 50%)`);
590
+
591
+ ctx.fillStyle = gradient;
592
+ ctx.fillRect(0, 0, width, height);
593
+
594
+ // Draw animated bars
595
+ const barCount = 32;
596
+ const barWidth = width / barCount;
597
+
598
+ for (let i = 0; i < barCount; i++) {
599
+ const barHeight = Math.sin(phase + i * 0.2) * height * 0.4 + height * 0.3;
600
+ const alpha = (Math.sin(phase + i * 0.1) + 1) * 0.5;
601
+
602
+ ctx.fillStyle = `rgba(255, 255, 255, ${alpha * 0.7})`;
603
+ ctx.fillRect(i * barWidth, height - barHeight, barWidth - 2, barHeight);
604
+ }
605
+ }
606
+
607
+ animateFallback();
608
+ }
609
+
610
+ function enhanceUIElements() {
611
+ // Add hover effects and animations to various UI elements
612
+ const elementsToEnhance = [
613
+ '.Button-sc-1dqy6sn-0', // Buttons
614
+ '.Card-sc-1uyc430-0', // Cards
615
+ '.main-card-card', // Main cards
616
+ '.main-shelf-shelf', // Shelves
617
+ '.main-trackList-row', // Track rows
618
+ '.progress-bar__slider', // Progress slider
619
+ '.volume-bar__slider' // Volume slider
620
+ ];
621
+
622
+ elementsToEnhance.forEach(selector => {
623
+ document.querySelectorAll(selector).forEach(element => {
624
+ element.style.transition = 'all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)';
625
+ });
626
+ });
627
+
628
+ // Add gradient text to important elements
629
+ document.querySelectorAll('.Type__TypeElement-sc-goli3j-0').forEach(text => {
630
+ if (text.textContent.length > 3 && text.textContent.length < 30) {
631
+ text.classList.add('glow-text');
632
+ }
633
+ });
634
+
635
+ // Add pulse animation to play button
636
+ const playButton = document.querySelector('.Button-sc-1dqy6sn-0.Lsg1a0PdZMXQ3vQZlE0_M');
637
+ if (playButton) {
638
+ playButton.style.animation = 'pulse 2s infinite';
639
+ }
640
+
641
+ // MutationObserver for dynamic content
642
+ const uiObserver = new MutationObserver(mutations => {
643
+ mutations.forEach(mutation => {
644
+ if (mutation.addedNodes.length) {
645
+ // Re-apply enhancements to new elements
646
+ elementsToEnhance.forEach(selector => {
647
+ document.querySelectorAll(selector).forEach(element => {
648
+ if (!element.style.transition) {
649
+ element.style.transition = 'all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)';
650
+ }
651
+ });
652
+ });
653
+ }
654
+ });
655
+ });
656
+
657
+ uiObserver.observe(document.body, {
658
+ childList: true,
659
+ subtree: true
660
+ });
661
+
662
+ // Cleanup on page unload
663
+ window.addEventListener('beforeunload', () => {
664
+ uiObserver.disconnect();
665
+ });
666
+ }
667
  })();