jk12p commited on
Commit
40db71e
·
verified ·
1 Parent(s): 809496a

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +692 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Togethertunes
3
- emoji: 🏃
4
- colorFrom: green
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: togethertunes
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,692 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TogetherTunes - Music Player for Couples</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
11
+
12
+ body {
13
+ font-family: 'Poppins', sans-serif;
14
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
15
+ height: 100vh;
16
+ overflow: hidden;
17
+ }
18
+
19
+ .heart-beat {
20
+ animation: heartbeat 1.5s infinite;
21
+ }
22
+
23
+ @keyframes heartbeat {
24
+ 0% { transform: scale(1); }
25
+ 25% { transform: scale(1.1); }
26
+ 50% { transform: scale(1); }
27
+ 75% { transform: scale(1.1); }
28
+ 100% { transform: scale(1); }
29
+ }
30
+
31
+ .progress-bar {
32
+ height: 4px;
33
+ background: #e2e8f0;
34
+ border-radius: 2px;
35
+ cursor: pointer;
36
+ }
37
+
38
+ .progress {
39
+ height: 100%;
40
+ background: #f43f5e;
41
+ border-radius: 2px;
42
+ transition: width 0.1s linear;
43
+ }
44
+
45
+ .album-art {
46
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
47
+ transition: transform 0.3s ease;
48
+ }
49
+
50
+ .album-art:hover {
51
+ transform: scale(1.05);
52
+ }
53
+
54
+ .song-item.active {
55
+ background-color: #fecdd3;
56
+ }
57
+
58
+ .song-item:hover:not(.active) {
59
+ background-color: #f8fafc;
60
+ }
61
+
62
+ .rotate {
63
+ animation: rotation 10s infinite linear;
64
+ }
65
+
66
+ @keyframes rotation {
67
+ from { transform: rotate(0deg); }
68
+ to { transform: rotate(359deg); }
69
+ }
70
+
71
+ #partnerStatus {
72
+ transition: all 0.3s ease;
73
+ }
74
+
75
+ .partner-connected {
76
+ background-color: #bbf7d0;
77
+ border-color: #4ade80;
78
+ }
79
+
80
+ .partner-disconnected {
81
+ background-color: #fee2e2;
82
+ border-color: #f87171;
83
+ }
84
+
85
+ .typing-indicator {
86
+ display: inline-block;
87
+ width: 10px;
88
+ height: 10px;
89
+ border-radius: 50%;
90
+ background-color: #f43f5e;
91
+ margin-right: 4px;
92
+ animation: typingAnimation 1.4s infinite ease-in-out;
93
+ }
94
+
95
+ .typing-indicator:nth-child(2) {
96
+ animation-delay: 0.2s;
97
+ }
98
+
99
+ .typing-indicator:nth-child(3) {
100
+ animation-delay: 0.4s;
101
+ }
102
+
103
+ @keyframes typingAnimation {
104
+ 0%, 60%, 100% { transform: translateY(0); }
105
+ 30% { transform: translateY(-5px); }
106
+ }
107
+ </style>
108
+ </head>
109
+ <body class="text-gray-800">
110
+ <div class="container mx-auto h-screen flex flex-col">
111
+ <!-- Header -->
112
+ <header class="py-4 px-6 bg-white bg-opacity-80 backdrop-blur-sm shadow-sm flex items-center justify-between">
113
+ <div class="flex items-center">
114
+ <i class="fas fa-heart text-pink-500 text-2xl mr-2 heart-beat"></i>
115
+ <h1 class="text-xl font-bold text-pink-600">TogetherTunes</h1>
116
+ </div>
117
+ <div class="flex items-center space-x-4">
118
+ <button id="uploadBtn" class="bg-pink-500 hover:bg-pink-600 text-white px-4 py-2 rounded-full text-sm font-medium transition flex items-center">
119
+ <i class="fas fa-upload mr-2"></i>
120
+ Upload Song
121
+ </button>
122
+ <input type="file" id="fileInput" accept="audio/*" class="hidden">
123
+ <div class="relative">
124
+ <img src="https://randomuser.me/api/portraits/women/44.jpg" alt="Profile" class="w-10 h-10 rounded-full object-cover border-2 border-pink-300">
125
+ </div>
126
+ </div>
127
+ </header>
128
+
129
+ <div class="flex flex-1 overflow-hidden">
130
+ <!-- Music Player Section -->
131
+ <div class="w-full p-6 flex flex-col">
132
+ <div class="bg-white rounded-xl shadow-md p-6 flex-1 flex flex-col">
133
+ <!-- Room Connectivity Section -->
134
+ <div class="mb-6 bg-white border rounded-lg p-4" id="partnerStatus">
135
+ <div class="flex items-center justify-between mb-2">
136
+ <h3 class="font-medium text-pink-600">Connection with Partner</h3>
137
+ <div id="connectionStatus" class="text-sm px-2 py-1 rounded-full bg-gray-100 text-gray-600">
138
+ Disconnected
139
+ </div>
140
+ </div>
141
+
142
+ <div class="flex items-center space-x-4">
143
+ <div class="flex-1">
144
+ <div class="flex items-center space-x-2 mb-2">
145
+ <input type="text" id="roomId" placeholder="Enter Room ID" class="border border-gray-300 rounded-full px-4 py-2 flex-1 focus:outline-none focus:ring-2 focus:ring-pink-300 focus:border-transparent">
146
+ <button id="connectBtn" class="bg-pink-500 hover:bg-pink-600 text-white px-4 py-2 rounded-full text-sm font-medium transition">
147
+ Connect
148
+ </button>
149
+ </div>
150
+ <div class="text-xs text-gray-500">
151
+ <p>Share this Room ID with your partner to sync your music experience</p>
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ <div id="partnerInfo" class="hidden mt-4 pt-4 border-t border-gray-100">
157
+ <div class="flex items-center space-x-3">
158
+ <img src="https://randomuser.me/api/portraits/men/32.jpg" alt="Partner" class="w-10 h-10 rounded-full object-cover border-2 border-pink-300">
159
+ <div>
160
+ <p class="font-medium">Partner Name</p>
161
+ <p class="text-xs text-gray-500 flex items-center" id="partnerActivity">
162
+ <span>Connected</span>
163
+ </p>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ </div>
168
+
169
+ <div class="flex-1 flex flex-col items-center justify-center" id="playerSection">
170
+ <div class="album-art w-64 h-64 rounded-full overflow-hidden mb-6 relative">
171
+ <img src="https://source.unsplash.com/random/300x300/?music" alt="Album Art" class="w-full h-full object-cover" id="albumArt">
172
+ <div class="absolute inset-0 bg-black bg-opacity-20 flex items-center justify-center">
173
+ <i class="fas fa-music text-white text-4xl"></i>
174
+ </div>
175
+ </div>
176
+
177
+ <div class="text-center mb-6">
178
+ <h3 class="text-lg font-semibold" id="songTitle">No song selected</h3>
179
+ <p class="text-gray-600" id="songArtist"></p>
180
+ </div>
181
+
182
+ <div class="w-full mb-4">
183
+ <div class="progress-bar" id="progressBar">
184
+ <div class="progress" id="progress"></div>
185
+ </div>
186
+ <div class="flex justify-between text-xs text-gray-500 mt-1">
187
+ <span id="currentTime">0:00</span>
188
+ <span id="duration">0:00</span>
189
+ </div>
190
+ </div>
191
+
192
+ <div class="flex items-center justify-center space-x-6 mb-8">
193
+ <button id="prevBtn" class="text-gray-600 hover:text-pink-500 transition">
194
+ <i class="fas fa-step-backward text-2xl"></i>
195
+ </button>
196
+ <button id="playBtn" class="bg-pink-500 hover:bg-pink-600 text-white rounded-full w-12 h-12 flex items-center justify-center transition">
197
+ <i class="fas fa-play text-xl" id="playIcon"></i>
198
+ </button>
199
+ <button id="nextBtn" class="text-gray-600 hover:text-pink-500 transition">
200
+ <i class="fas fa-step-forward text-2xl"></i>
201
+ </button>
202
+ </div>
203
+
204
+ <div class="flex items-center justify-between w-full max-w-xs">
205
+ <button id="likeBtn" class="text-gray-600 hover:text-pink-500 transition">
206
+ <i class="far fa-heart text-xl"></i>
207
+ </button>
208
+ <button id="volumeBtn" class="text-gray-600 hover:text-pink-500 transition">
209
+ <i class="fas fa-volume-up text-xl"></i>
210
+ </button>
211
+ <input type="range" id="volumeControl" min="0" max="1" step="0.01" value="1" class="w-20">
212
+ <button class="text-gray-600 hover:text-pink-500 transition">
213
+ <i class="fas fa-ellipsis-h text-xl"></i>
214
+ </button>
215
+ </div>
216
+ </div>
217
+ </div>
218
+
219
+ <div class="mt-4 bg-white rounded-xl shadow-md p-4">
220
+ <h3 class="font-medium mb-2 text-pink-600">Playlist</h3>
221
+ <div class="space-y-2 max-h-60 overflow-y-auto" id="playlist">
222
+ <div class="text-center py-4 text-gray-500">
223
+ <i class="fas fa-music mr-2"></i> Your playlist is empty
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </div>
230
+
231
+ <script>
232
+ // Audio player functionality
233
+ const audio = new Audio();
234
+ let currentSongIndex = -1;
235
+ let songs = [];
236
+
237
+ // Room connection simulation
238
+ let isConnected = false;
239
+ let roomId = '';
240
+ let partnerTyping = false;
241
+
242
+ // DOM elements
243
+ const playBtn = document.getElementById('playBtn');
244
+ const playIcon = document.getElementById('playIcon');
245
+ const progressBar = document.getElementById('progressBar');
246
+ const progress = document.getElementById('progress');
247
+ const currentTimeEl = document.getElementById('currentTime');
248
+ const durationEl = document.getElementById('duration');
249
+ const prevBtn = document.getElementById('prevBtn');
250
+ const nextBtn = document.getElementById('nextBtn');
251
+ const likeBtn = document.getElementById('likeBtn');
252
+ const albumArt = document.getElementById('albumArt');
253
+ const songTitle = document.getElementById('songTitle');
254
+ const songArtist = document.getElementById('songArtist');
255
+ const playlist = document.getElementById('playlist');
256
+ const uploadBtn = document.getElementById('uploadBtn');
257
+ const fileInput = document.getElementById('fileInput');
258
+ const volumeBtn = document.getElementById('volumeBtn');
259
+ const volumeControl = document.getElementById('volumeControl');
260
+
261
+ // Room connection elements
262
+ const partnerStatus = document.getElementById('partnerStatus');
263
+ const connectionStatus = document.getElementById('connectionStatus');
264
+ const roomIdInput = document.getElementById('roomId');
265
+ const connectBtn = document.getElementById('connectBtn');
266
+ const partnerInfo = document.getElementById('partnerInfo');
267
+ const partnerActivity = document.getElementById('partnerActivity');
268
+
269
+ // Format time (seconds to MM:SS)
270
+ function formatTime(seconds) {
271
+ const mins = Math.floor(seconds / 60);
272
+ const secs = Math.floor(seconds % 60);
273
+ return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
274
+ }
275
+
276
+ // Render playlist
277
+ function renderPlaylist() {
278
+ playlist.innerHTML = '';
279
+
280
+ if (songs.length === 0) {
281
+ playlist.innerHTML = '<div class="text-center py-4 text-gray-500"><i class="fas fa-music mr-2"></i> Your playlist is empty</div>';
282
+ return;
283
+ }
284
+
285
+ songs.forEach((song, index) => {
286
+ const songElement = document.createElement('div');
287
+ songElement.className = `flex items-center p-2 rounded-lg cursor-pointer song-item ${currentSongIndex === index ? 'active' : ''}`;
288
+ songElement.innerHTML = `
289
+ <img src="${song.cover || 'https://source.unsplash.com/random/100x100/?music'}" alt="Song" class="w-10 h-10 rounded mr-3">
290
+ <div class="flex-1">
291
+ <p class="text-sm font-medium">${song.title || 'Unknown Title'}</p>
292
+ <p class="text-xs text-gray-500">${song.artist || 'Unknown Artist'}</p>
293
+ </div>
294
+ <div class="flex items-center">
295
+ <i class="fas fa-heart ${song.liked ? 'text-pink-500' : 'text-gray-300'} mr-3"></i>
296
+ <span class="text-xs text-gray-400">${formatTime(song.duration || 0)}</span>
297
+ </div>
298
+ `;
299
+
300
+ songElement.addEventListener('click', () => {
301
+ playSong(index);
302
+ });
303
+
304
+ playlist.appendChild(songElement);
305
+ });
306
+ }
307
+
308
+ // Play a specific song
309
+ function playSong(index) {
310
+ if (index < 0 || index >= songs.length) return;
311
+
312
+ currentSongIndex = index;
313
+ const song = songs[currentSongIndex];
314
+
315
+ // Update UI
316
+ songTitle.textContent = song.title || 'Unknown Title';
317
+ songArtist.textContent = song.artist || 'Unknown Artist';
318
+ albumArt.src = song.cover || 'https://source.unsplash.com/random/300x300/?music';
319
+ durationEl.textContent = formatTime(song.duration || 0);
320
+
321
+ // Update active song in playlist
322
+ document.querySelectorAll('.song-item').forEach((item, i) => {
323
+ if (i === currentSongIndex) {
324
+ item.classList.add('active');
325
+ } else {
326
+ item.classList.remove('active');
327
+ }
328
+ });
329
+
330
+ // Update like button
331
+ likeBtn.innerHTML = `<i class="${song.liked ? 'fas' : 'far'} fa-heart text-xl"></i>`;
332
+
333
+ // Load and play audio
334
+ if (song.audio instanceof Blob) {
335
+ // For uploaded files
336
+ const audioURL = URL.createObjectURL(song.audio);
337
+ audio.src = audioURL;
338
+ } else {
339
+ // For other sources
340
+ audio.src = song.audio;
341
+ }
342
+
343
+ audio.load();
344
+ audio.play()
345
+ .then(() => {
346
+ playIcon.classList.remove('fa-play');
347
+ playIcon.classList.add('fa-pause');
348
+
349
+ // Start album art rotation
350
+ document.querySelector('.album-art').classList.add('rotate');
351
+
352
+ // Simulate sending song info to partner
353
+ if (isConnected) {
354
+ simulatePartnerActivity('listening to ' + (song.title || 'Unknown Title'));
355
+ }
356
+ })
357
+ .catch(error => {
358
+ console.error('Playback failed:', error);
359
+ });
360
+ }
361
+
362
+ // Play next song
363
+ function playNext() {
364
+ if (songs.length === 0) return;
365
+
366
+ if (currentSongIndex < songs.length - 1) {
367
+ playSong(currentSongIndex + 1);
368
+ } else {
369
+ playSong(0); // Loop back to first song
370
+ }
371
+ }
372
+
373
+ // Play previous song
374
+ function playPrev() {
375
+ if (songs.length === 0) return;
376
+
377
+ if (currentSongIndex > 0) {
378
+ playSong(currentSongIndex - 1);
379
+ } else {
380
+ playSong(songs.length - 1); // Go to last song
381
+ }
382
+ }
383
+
384
+ // Toggle like status for current song
385
+ function toggleLike() {
386
+ if (currentSongIndex === -1) return;
387
+
388
+ songs[currentSongIndex].liked = !songs[currentSongIndex].liked;
389
+ likeBtn.innerHTML = `<i class="${songs[currentSongIndex].liked ? 'fas' : 'far'} fa-heart text-xl"></i>`;
390
+
391
+ // Update in playlist
392
+ const heartIcons = document.querySelectorAll('.song-item .fa-heart');
393
+ if (heartIcons[currentSongIndex]) {
394
+ heartIcons[currentSongIndex].className = songs[currentSongIndex].liked ?
395
+ 'fas fa-heart text-pink-500 mr-3' : 'far fa-heart text-gray-300 mr-3';
396
+ }
397
+
398
+ // Simulate partner reaction
399
+ if (isConnected) {
400
+ simulatePartnerTyping();
401
+ setTimeout(() => {
402
+ partnerActivity.innerHTML = songs[currentSongIndex].liked ?
403
+ '<span>❤️ Loved this song too!</span>' :
404
+ '<span>😢 Why don\'t you like this song?</span>';
405
+ }, 1500);
406
+ }
407
+ }
408
+
409
+ // Handle file upload
410
+ uploadBtn.addEventListener('click', () => {
411
+ fileInput.click();
412
+ });
413
+
414
+ fileInput.addEventListener('change', (e) => {
415
+ const files = e.target.files;
416
+ if (files.length === 0) return;
417
+
418
+ const file = files[0];
419
+ const newSong = {
420
+ id: Date.now(),
421
+ title: file.name.replace(/\.[^/.]+$/, ""), // Remove file extension
422
+ artist: 'Uploaded Song',
423
+ duration: 0, // Will be updated when metadata loads
424
+ cover: 'https://source.unsplash.com/random/300x300/?music',
425
+ audio: file,
426
+ liked: false
427
+ };
428
+
429
+ // Add to playlist
430
+ songs.push(newSong);
431
+ renderPlaylist();
432
+
433
+ // If this is the first song, play it
434
+ if (songs.length === 1) {
435
+ playSong(0);
436
+ }
437
+
438
+ // Try to get duration from the file
439
+ const audioForDuration = new Audio();
440
+ audioForDuration.src = URL.createObjectURL(file);
441
+ audioForDuration.addEventListener('loadedmetadata', () => {
442
+ newSong.duration = audioForDuration.duration;
443
+ renderPlaylist();
444
+ });
445
+ });
446
+
447
+ // Volume control
448
+ volumeControl.addEventListener('input', () => {
449
+ audio.volume = volumeControl.value;
450
+
451
+ // Update volume icon based on level
452
+ if (volumeControl.value == 0) {
453
+ volumeBtn.innerHTML = '<i class="fas fa-volume-mute text-xl"></i>';
454
+ } else if (volumeControl.value < 0.5) {
455
+ volumeBtn.innerHTML = '<i class="fas fa-volume-down text-xl"></i>';
456
+ } else {
457
+ volumeBtn.innerHTML = '<i class="fas fa-volume-up text-xl"></i>';
458
+ }
459
+ });
460
+
461
+ // Toggle mute
462
+ volumeBtn.addEventListener('click', () => {
463
+ if (audio.volume > 0) {
464
+ audio.volume = 0;
465
+ volumeControl.value = 0;
466
+ volumeBtn.innerHTML = '<i class="fas fa-volume-mute text-xl"></i>';
467
+ } else {
468
+ audio.volume = 1;
469
+ volumeControl.value = 1;
470
+ volumeBtn.innerHTML = '<i class="fas fa-volume-up text-xl"></i>';
471
+ }
472
+ });
473
+
474
+ // Update progress bar
475
+ audio.addEventListener('timeupdate', () => {
476
+ const { currentTime, duration } = audio;
477
+ const progressPercent = (currentTime / duration) * 100;
478
+ progress.style.width = `${progressPercent}%`;
479
+ currentTimeEl.textContent = formatTime(currentTime);
480
+
481
+ // Update duration if not already set
482
+ if (durationEl.textContent === '0:00' && !isNaN(duration)) {
483
+ durationEl.textContent = formatTime(duration);
484
+
485
+ // Update duration in current song object
486
+ if (currentSongIndex !== -1) {
487
+ songs[currentSongIndex].duration = duration;
488
+ renderPlaylist();
489
+ }
490
+ }
491
+ });
492
+
493
+ // Set song duration when metadata is loaded
494
+ audio.addEventListener('loadedmetadata', () => {
495
+ durationEl.textContent = formatTime(audio.duration);
496
+ });
497
+
498
+ // When song ends, play next one
499
+ audio.addEventListener('ended', playNext);
500
+
501
+ // Click on progress bar to seek
502
+ progressBar.addEventListener('click', (e) => {
503
+ const width = progressBar.clientWidth;
504
+ const clickX = e.offsetX;
505
+ const duration = audio.duration;
506
+ audio.currentTime = (clickX / width) * duration;
507
+
508
+ // Simulate partner activity
509
+ if (isConnected) {
510
+ simulatePartnerActivity('changed song position');
511
+ }
512
+ });
513
+
514
+ // Play/pause toggle
515
+ playBtn.addEventListener('click', () => {
516
+ if (songs.length === 0) {
517
+ alert('Please add songs to your playlist first');
518
+ return;
519
+ }
520
+
521
+ if (currentSongIndex === -1) {
522
+ playSong(0);
523
+ return;
524
+ }
525
+
526
+ if (audio.paused) {
527
+ audio.play();
528
+ playIcon.classList.remove('fa-play');
529
+ playIcon.classList.add('fa-pause');
530
+ document.querySelector('.album-art').classList.add('rotate');
531
+
532
+ // Simulate partner activity
533
+ if (isConnected) {
534
+ simulatePartnerActivity('resumed the song');
535
+ }
536
+ } else {
537
+ audio.pause();
538
+ playIcon.classList.remove('fa-pause');
539
+ playIcon.classList.add('fa-play');
540
+ document.querySelector('.album-art').classList.remove('rotate');
541
+
542
+ // Simulate partner activity
543
+ if (isConnected) {
544
+ simulatePartnerActivity('paused the song');
545
+ }
546
+ }
547
+ });
548
+
549
+ // Navigation controls
550
+ prevBtn.addEventListener('click', playPrev);
551
+ nextBtn.addEventListener('click', playNext);
552
+ likeBtn.addEventListener('click', toggleLike);
553
+
554
+ // Room connection functionality
555
+ connectBtn.addEventListener('click', () => {
556
+ const newRoomId = roomIdInput.value.trim();
557
+
558
+ if (!newRoomId) {
559
+ alert('Please enter a Room ID');
560
+ return;
561
+ }
562
+
563
+ // Simulate connection process
564
+ connectionStatus.textContent = 'Connecting...';
565
+ connectionStatus.className = 'text-sm px-2 py-1 rounded-full bg-yellow-100 text-yellow-600';
566
+
567
+ setTimeout(() => {
568
+ // Successful connection
569
+ isConnected = true;
570
+ roomId = newRoomId;
571
+ connectionStatus.textContent = 'Connected';
572
+ connectionStatus.className = 'text-sm px-2 py-1 rounded-full bg-green-100 text-green-600';
573
+ partnerStatus.classList.add('partner-connected');
574
+ partnerStatus.classList.remove('partner-disconnected');
575
+
576
+ // Show partner info
577
+ partnerInfo.classList.remove('hidden');
578
+
579
+ // Simulate partner joining
580
+ setTimeout(() => {
581
+ partnerActivity.innerHTML = '<span>Joined the room</span>';
582
+
583
+ // Simulate partner typing
584
+ setTimeout(() => {
585
+ simulatePartnerTyping();
586
+ setTimeout(() => {
587
+ partnerActivity.innerHTML = '<span>Hey there! Ready to listen together? ❤️</span>';
588
+ }, 2000);
589
+ }, 1500);
590
+ }, 500);
591
+
592
+ // Change button to disconnect
593
+ connectBtn.textContent = 'Disconnect';
594
+ connectBtn.className = 'bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm font-medium transition';
595
+
596
+ // Disable room ID input
597
+ roomIdInput.disabled = true;
598
+ }, 1500);
599
+ });
600
+
601
+ // Toggle connection
602
+ connectBtn.addEventListener('click', function() {
603
+ if (isConnected) {
604
+ // Disconnect
605
+ isConnected = false;
606
+ connectionStatus.textContent = 'Disconnected';
607
+ connectionStatus.className = 'text-sm px-2 py-1 rounded-full bg-gray-100 text-gray-600';
608
+ partnerStatus.classList.remove('partner-connected');
609
+ partnerStatus.classList.add('partner-disconnected');
610
+
611
+ // Hide partner info
612
+ partnerInfo.classList.add('hidden');
613
+
614
+ // Change button back to connect
615
+ this.textContent = 'Connect';
616
+ this.className = 'bg-pink-500 hover:bg-pink-600 text-white px-4 py-2 rounded-full text-sm font-medium transition';
617
+
618
+ // Enable room ID input
619
+ roomIdInput.disabled = false;
620
+ roomId = '';
621
+ }
622
+ });
623
+
624
+ // Simulate partner typing
625
+ function simulatePartnerTyping() {
626
+ partnerTyping = true;
627
+ partnerActivity.innerHTML = `
628
+ <span>Partner is typing</span>
629
+ <span class="typing-indicator"></span>
630
+ <span class="typing-indicator"></span>
631
+ <span class="typing-indicator"></span>
632
+ `;
633
+ }
634
+
635
+ // Simulate partner activity
636
+ function simulatePartnerActivity(activity) {
637
+ if (!isConnected) return;
638
+
639
+ partnerTyping = true;
640
+ partnerActivity.innerHTML = `
641
+ <span>Partner is typing</span>
642
+ <span class="typing-indicator"></span>
643
+ <span class="typing-indicator"></span>
644
+ <span class="typing-indicator"></span>
645
+ `;
646
+
647
+ setTimeout(() => {
648
+ partnerTyping = false;
649
+ partnerActivity.innerHTML = `<span>${activity}</span>`;
650
+ }, 1500);
651
+ }
652
+
653
+ // Initialize volume
654
+ audio.volume = volumeControl.value;
655
+
656
+ // Add some sample songs for demo
657
+ window.addEventListener('DOMContentLoaded', () => {
658
+ songs = [
659
+ {
660
+ id: 1,
661
+ title: 'Perfect',
662
+ artist: 'Ed Sheeran',
663
+ duration: 263,
664
+ cover: 'https://source.unsplash.com/random/300x300/?love',
665
+ audio: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
666
+ liked: false
667
+ },
668
+ {
669
+ id: 2,
670
+ title: 'All of Me',
671
+ artist: 'John Legend',
672
+ duration: 271,
673
+ cover: 'https://source.unsplash.com/random/300x300/?romantic',
674
+ audio: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3',
675
+ liked: false
676
+ },
677
+ {
678
+ id: 3,
679
+ title: 'A Thousand Years',
680
+ artist: 'Christina Perri',
681
+ duration: 295,
682
+ cover: 'https://source.unsplash.com/random/300x300/?wedding',
683
+ audio: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3',
684
+ liked: false
685
+ }
686
+ ];
687
+
688
+ renderPlaylist();
689
+ });
690
+ </script>
691
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=jk12p/togethertunes" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
692
+ </html>