nocopyrightsounds-widget 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/index.js +69 -41
package/README.md
CHANGED
|
@@ -15,6 +15,6 @@ import NCSWidget from 'nocopyrightsounds-widget';
|
|
|
15
15
|
|
|
16
16
|
const widget = new NCSWidget({
|
|
17
17
|
position: 'bottom-right', // 'bottom-left', 'top-right', 'top-left'
|
|
18
|
-
apiUrl: 'https://ncs-api.
|
|
18
|
+
apiUrl: 'https://ncs-backend-api.onrender.com' // L'URL de votre API
|
|
19
19
|
});
|
|
20
20
|
\`\`\`
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -6,7 +6,6 @@ class NCSWidget {
|
|
|
6
6
|
this.audio = new Audio();
|
|
7
7
|
this.isPlaying = false;
|
|
8
8
|
|
|
9
|
-
// Historique et File d'attente (Préchargement)
|
|
10
9
|
this.trackHistory = [];
|
|
11
10
|
this.currentHistoryIndex = -1;
|
|
12
11
|
this.nextTracksQueue = [];
|
|
@@ -14,21 +13,25 @@ class NCSWidget {
|
|
|
14
13
|
|
|
15
14
|
this.isBrowser = typeof window !== 'undefined';
|
|
16
15
|
if (this.isBrowser) {
|
|
17
|
-
|
|
16
|
+
// Le volume est à 1 (100%) par défaut s'il n'y a rien en cache
|
|
17
|
+
const savedVol = localStorage.getItem('ncs_volume');
|
|
18
|
+
this.audio.volume = savedVol !== null ? parseFloat(savedVol) : 1.0;
|
|
19
|
+
this.lastVolume = this.audio.volume > 0 ? this.audio.volume : 1.0; // Pour le mute/unmute
|
|
20
|
+
|
|
18
21
|
this.savedTime = localStorage.getItem('ncs_currentTime') || 0;
|
|
19
22
|
this.savedTrack = localStorage.getItem('ncs_currentTrack') || null;
|
|
20
23
|
this.savedCover = localStorage.getItem('ncs_currentCover') || null;
|
|
21
24
|
this.savedTitle = localStorage.getItem('ncs_currentTitle') || null;
|
|
25
|
+
this.savedArtists = localStorage.getItem('ncs_currentArtists') || null;
|
|
22
26
|
this.isWidgetOpen = localStorage.getItem('ncs_isOpen') === 'true';
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
this.initDOM();
|
|
26
30
|
this.attachEvents();
|
|
27
31
|
|
|
28
|
-
// Initialisation de la musique
|
|
29
32
|
if (this.savedTrack && this.savedTime > 0) {
|
|
30
33
|
this.restoreTrack();
|
|
31
|
-
this.fillQueue(this.genreSelect.value);
|
|
34
|
+
this.fillQueue(this.genreSelect.value);
|
|
32
35
|
} else {
|
|
33
36
|
this.changeGenre(this.genreSelect.value);
|
|
34
37
|
}
|
|
@@ -43,23 +46,18 @@ class NCSWidget {
|
|
|
43
46
|
const style = document.createElement('style');
|
|
44
47
|
style.textContent = `
|
|
45
48
|
#ncs-persistent-widget { position: fixed; ${this.getPositionStyles()} z-index: 99999; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
|
|
46
|
-
|
|
47
|
-
/* État Réduit */
|
|
48
49
|
.ncs-minimized { width: 55px; height: 55px; border-radius: 50%; background: linear-gradient(135deg, #1DB954, #1ed760); cursor: pointer; display: flex; justify-content: center; align-items: center; box-shadow: 0 6px 15px rgba(29, 185, 84, 0.4); font-size: 24px; transition: transform 0.2s; }
|
|
49
50
|
.ncs-minimized:hover { transform: scale(1.1); }
|
|
50
51
|
.ncs-minimized.hidden { display: none; }
|
|
51
52
|
|
|
52
|
-
/* État Agrandit */
|
|
53
53
|
.ncs-expanded { width: 320px; background: #181818; color: white; border-radius: 16px; padding: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); display: none; border: 1px solid #282828; }
|
|
54
54
|
.ncs-expanded.active { display: block; animation: ncsFadeIn 0.3s ease; }
|
|
55
55
|
|
|
56
|
-
/* Header & Infos */
|
|
57
56
|
.ncs-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
|
|
58
57
|
.ncs-header strong { font-size: 14px; font-weight: 600; color: #b3b3b3; letter-spacing: 1px; text-transform: uppercase; display: flex; align-items: center; gap: 8px; }
|
|
59
58
|
.ncs-close-btn { background: transparent; border: none; color: #b3b3b3; font-size: 18px; cursor: pointer; padding: 0; transition: color 0.2s; }
|
|
60
59
|
.ncs-close-btn:hover { color: white; }
|
|
61
60
|
|
|
62
|
-
/* Animation Visualizer */
|
|
63
61
|
.ncs-visualizer { display: flex; gap: 2px; height: 12px; align-items: flex-end; opacity: 0; transition: opacity 0.3s; }
|
|
64
62
|
.ncs-visualizer.playing { opacity: 1; }
|
|
65
63
|
.ncs-bar { width: 3px; background: #1DB954; border-radius: 2px; animation: bounce 0.5s infinite alternate; }
|
|
@@ -69,11 +67,11 @@ class NCSWidget {
|
|
|
69
67
|
|
|
70
68
|
.ncs-track-info { display: flex; align-items: center; margin-bottom: 15px; }
|
|
71
69
|
.ncs-cover { width: 65px; height: 65px; border-radius: 8px; background: #282828; margin-right: 15px; object-fit: cover; box-shadow: 0 4px 10px rgba(0,0,0,0.3); }
|
|
72
|
-
.ncs-details { flex: 1; overflow: hidden; }
|
|
73
|
-
#ncs-track-name { font-size: 14px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom:
|
|
70
|
+
.ncs-details { flex: 1; overflow: hidden; display: flex; flex-direction: column; justify-content: center; }
|
|
71
|
+
#ncs-track-name { font-size: 14px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 2px; }
|
|
72
|
+
#ncs-artists { font-size: 11px; color: #a0a0a0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 8px; }
|
|
74
73
|
#ncs-genre { width: 100%; padding: 4px 8px; background: #282828; color: #b3b3b3; border: 1px solid #333; border-radius: 4px; font-size: 12px; cursor: pointer; outline: none; }
|
|
75
74
|
|
|
76
|
-
/* Progress & Controls */
|
|
77
75
|
.ncs-progress-container { margin-bottom: 15px; display:flex; align-items:center; gap: 10px; font-size: 11px; color: #b3b3b3; }
|
|
78
76
|
.ncs-slider { -webkit-appearance: none; width: 100%; height: 4px; background: #535353; border-radius: 2px; outline: none; cursor: pointer; }
|
|
79
77
|
.ncs-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #1DB954; cursor: pointer; transition: transform 0.1s; }
|
|
@@ -89,6 +87,8 @@ class NCSWidget {
|
|
|
89
87
|
|
|
90
88
|
.ncs-bottom-bar { display: flex; justify-content: space-between; align-items: center; }
|
|
91
89
|
.ncs-volume-container { display: flex; align-items: center; gap: 8px; color: #b3b3b3; flex: 1; margin-right: 15px; }
|
|
90
|
+
#ncs-mute-btn { cursor: pointer; transition: transform 0.1s; user-select: none; }
|
|
91
|
+
#ncs-mute-btn:hover { transform: scale(1.1); }
|
|
92
92
|
.ncs-download-btn { color: #b3b3b3; text-decoration: none; font-size: 18px; transition: color 0.2s; }
|
|
93
93
|
.ncs-download-btn:hover { color: #1DB954; }
|
|
94
94
|
|
|
@@ -99,8 +99,7 @@ class NCSWidget {
|
|
|
99
99
|
<div class="ncs-minimized ${this.isWidgetOpen ? 'hidden' : ''}">🎧</div>
|
|
100
100
|
<div class="ncs-expanded ${this.isWidgetOpen ? 'active' : ''}">
|
|
101
101
|
<div class="ncs-header">
|
|
102
|
-
<strong>
|
|
103
|
-
NCS Player
|
|
102
|
+
<strong>NCS Player
|
|
104
103
|
<div class="ncs-visualizer" id="ncs-vis">
|
|
105
104
|
<div class="ncs-bar"></div><div class="ncs-bar"></div><div class="ncs-bar"></div>
|
|
106
105
|
</div>
|
|
@@ -112,6 +111,7 @@ class NCSWidget {
|
|
|
112
111
|
<img id="ncs-cover" class="ncs-cover" src="data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Cover" />
|
|
113
112
|
<div class="ncs-details">
|
|
114
113
|
<div id="ncs-track-name">Chargement...</div>
|
|
114
|
+
<div id="ncs-artists">Artiste(s)</div>
|
|
115
115
|
<select id="ncs-genre">
|
|
116
116
|
<option value="electronic">⚡ Électronique</option>
|
|
117
117
|
<option value="house">🏠 House</option>
|
|
@@ -135,7 +135,7 @@ class NCSWidget {
|
|
|
135
135
|
|
|
136
136
|
<div class="ncs-bottom-bar">
|
|
137
137
|
<div class="ncs-volume-container">
|
|
138
|
-
<span
|
|
138
|
+
<span id="ncs-mute-btn">${this.audio.volume === 0 ? '🔇' : '🔊'}</span>
|
|
139
139
|
<input type="range" id="ncs-volume" class="ncs-slider" min="0" max="1" step="0.05" value="${this.audio.volume}">
|
|
140
140
|
</div>
|
|
141
141
|
<a id="ncs-download" class="ncs-download-btn" href="#" target="_blank" title="Télécharger ce titre">⬇️</a>
|
|
@@ -152,9 +152,11 @@ class NCSWidget {
|
|
|
152
152
|
this.nextBtn = this.container.querySelector('#ncs-next');
|
|
153
153
|
this.prevBtn = this.container.querySelector('#ncs-prev');
|
|
154
154
|
this.downloadBtn = this.container.querySelector('#ncs-download');
|
|
155
|
+
this.muteBtn = this.container.querySelector('#ncs-mute-btn');
|
|
155
156
|
this.visualizer = this.container.querySelector('#ncs-vis');
|
|
156
157
|
this.genreSelect = this.container.querySelector('#ncs-genre');
|
|
157
158
|
this.trackName = this.container.querySelector('#ncs-track-name');
|
|
159
|
+
this.artistsName = this.container.querySelector('#ncs-artists');
|
|
158
160
|
this.coverImg = this.container.querySelector('#ncs-cover');
|
|
159
161
|
this.progressBar = this.container.querySelector('#ncs-progress');
|
|
160
162
|
this.volumeBar = this.container.querySelector('#ncs-volume');
|
|
@@ -201,10 +203,31 @@ class NCSWidget {
|
|
|
201
203
|
this.audio.addEventListener('ended', () => this.handleNext());
|
|
202
204
|
|
|
203
205
|
this.progressBar.addEventListener('input', (e) => { this.audio.currentTime = e.target.value; });
|
|
206
|
+
|
|
207
|
+
// --- NOUVEAU : GESTION DU MUTE ET DU VOLUME ---
|
|
204
208
|
this.volumeBar.addEventListener('input', (e) => {
|
|
205
|
-
|
|
206
|
-
|
|
209
|
+
const vol = parseFloat(e.target.value);
|
|
210
|
+
this.audio.volume = vol;
|
|
211
|
+
if (vol > 0) this.lastVolume = vol; // On mémorise si c'est > 0
|
|
212
|
+
this.updateMuteIcon(vol);
|
|
213
|
+
localStorage.setItem('ncs_volume', vol);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
this.muteBtn.addEventListener('click', () => {
|
|
217
|
+
if (this.audio.volume > 0) {
|
|
218
|
+
// On Mute
|
|
219
|
+
this.lastVolume = this.audio.volume;
|
|
220
|
+
this.audio.volume = 0;
|
|
221
|
+
this.volumeBar.value = 0;
|
|
222
|
+
} else {
|
|
223
|
+
// On Unmute
|
|
224
|
+
this.audio.volume = this.lastVolume || 1.0;
|
|
225
|
+
this.volumeBar.value = this.audio.volume;
|
|
226
|
+
}
|
|
227
|
+
this.updateMuteIcon(this.audio.volume);
|
|
228
|
+
localStorage.setItem('ncs_volume', this.audio.volume);
|
|
207
229
|
});
|
|
230
|
+
// ----------------------------------------------
|
|
208
231
|
|
|
209
232
|
setInterval(() => {
|
|
210
233
|
if (this.isPlaying && this.audio.currentTime > 0) {
|
|
@@ -213,6 +236,16 @@ class NCSWidget {
|
|
|
213
236
|
}, 1000);
|
|
214
237
|
}
|
|
215
238
|
|
|
239
|
+
updateMuteIcon(vol) {
|
|
240
|
+
if (vol === 0) {
|
|
241
|
+
this.muteBtn.innerText = '🔇';
|
|
242
|
+
} else if (vol < 0.5) {
|
|
243
|
+
this.muteBtn.innerText = '🔉';
|
|
244
|
+
} else {
|
|
245
|
+
this.muteBtn.innerText = '🔊';
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
216
249
|
toggleState(isOpen) {
|
|
217
250
|
if (isOpen) {
|
|
218
251
|
this.minimized.classList.add('hidden');
|
|
@@ -248,10 +281,6 @@ class NCSWidget {
|
|
|
248
281
|
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
|
|
249
282
|
}
|
|
250
283
|
|
|
251
|
-
// ----------------------------------------------------
|
|
252
|
-
// GESTION DES REQUÊTES ET DE LA FILE D'ATTENTE (NOUVEAU)
|
|
253
|
-
// ----------------------------------------------------
|
|
254
|
-
|
|
255
284
|
async fetchSingleTrack(genre) {
|
|
256
285
|
try {
|
|
257
286
|
const response = await fetch(`${this.apiUrl}/search?genre=${genre}`);
|
|
@@ -264,50 +293,47 @@ class NCSWidget {
|
|
|
264
293
|
}
|
|
265
294
|
|
|
266
295
|
async fillQueue(genre) {
|
|
267
|
-
if (this.isPreloading) return;
|
|
296
|
+
if (this.isPreloading) return;
|
|
268
297
|
this.isPreloading = true;
|
|
269
298
|
|
|
270
|
-
// Tant qu'on n'a pas 2 musiques d'avance, on cherche silencieusement
|
|
271
299
|
while (this.nextTracksQueue.length < 2) {
|
|
272
300
|
const track = await this.fetchSingleTrack(genre);
|
|
273
301
|
if (track) {
|
|
274
|
-
// Vérifier qu'on n'ajoute pas un doublon dans la file
|
|
275
302
|
const isDuplicate = this.nextTracksQueue.find(t => t.audioUrl === track.audioUrl);
|
|
276
303
|
if (!isDuplicate) this.nextTracksQueue.push(track);
|
|
277
304
|
} else {
|
|
278
|
-
break;
|
|
305
|
+
break;
|
|
279
306
|
}
|
|
280
307
|
}
|
|
281
|
-
|
|
282
308
|
this.isPreloading = false;
|
|
283
309
|
}
|
|
284
310
|
|
|
285
311
|
async changeGenre(genre) {
|
|
286
|
-
this.nextTracksQueue = [];
|
|
312
|
+
this.nextTracksQueue = [];
|
|
287
313
|
this.trackName.innerText = "Recherche...";
|
|
314
|
+
this.artistsName.innerText = "...";
|
|
288
315
|
|
|
289
316
|
const track = await this.fetchSingleTrack(genre);
|
|
290
317
|
if (track) {
|
|
291
318
|
this.setTrack(track, true);
|
|
292
|
-
this.fillQueue(genre);
|
|
319
|
+
this.fillQueue(genre);
|
|
293
320
|
} else {
|
|
294
|
-
this.trackName.innerText = "Aucune piste
|
|
321
|
+
this.trackName.innerText = "Aucune piste.";
|
|
322
|
+
this.artistsName.innerText = "";
|
|
295
323
|
}
|
|
296
324
|
}
|
|
297
325
|
|
|
298
|
-
// ----------------------------------------------------
|
|
299
|
-
// GESTION DU LECTEUR ET HISTORIQUE
|
|
300
|
-
// ----------------------------------------------------
|
|
301
|
-
|
|
302
326
|
restoreTrack() {
|
|
303
327
|
this.audio.src = this.savedTrack;
|
|
304
328
|
this.trackName.innerText = this.savedTitle;
|
|
329
|
+
this.artistsName.innerText = this.savedArtists || "NCS Release";
|
|
305
330
|
if (this.savedCover) this.coverImg.src = this.savedCover;
|
|
306
331
|
this.downloadBtn.href = this.savedTrack;
|
|
307
332
|
|
|
308
333
|
this.trackHistory = [{
|
|
309
334
|
audioUrl: this.savedTrack,
|
|
310
335
|
title: this.savedTitle,
|
|
336
|
+
artists: this.savedArtists,
|
|
311
337
|
coverUrl: this.savedCover
|
|
312
338
|
}];
|
|
313
339
|
this.currentHistoryIndex = 0;
|
|
@@ -316,11 +342,16 @@ class NCSWidget {
|
|
|
316
342
|
setTrack(track, addToHistory = false) {
|
|
317
343
|
this.audio.src = track.audioUrl;
|
|
318
344
|
this.trackName.innerText = track.title;
|
|
345
|
+
|
|
346
|
+
// Mettre à jour l'affichage des artistes
|
|
347
|
+
const artistes = track.artists || "NCS Release";
|
|
348
|
+
this.artistsName.innerText = artistes;
|
|
349
|
+
this.artistsName.title = artistes; // Bulle info au survol si le texte est long
|
|
350
|
+
|
|
319
351
|
if (track.coverUrl) this.coverImg.src = track.coverUrl;
|
|
320
352
|
this.downloadBtn.href = track.audioUrl;
|
|
321
353
|
|
|
322
354
|
if (addToHistory) {
|
|
323
|
-
// Effacer le "futur" si on était revenu en arrière avant d'avancer
|
|
324
355
|
this.trackHistory = this.trackHistory.slice(0, this.currentHistoryIndex + 1);
|
|
325
356
|
this.trackHistory.push(track);
|
|
326
357
|
this.currentHistoryIndex = this.trackHistory.length - 1;
|
|
@@ -330,6 +361,7 @@ class NCSWidget {
|
|
|
330
361
|
if (this.isBrowser) {
|
|
331
362
|
localStorage.setItem('ncs_currentTrack', track.audioUrl);
|
|
332
363
|
localStorage.setItem('ncs_currentTitle', track.title);
|
|
364
|
+
localStorage.setItem('ncs_currentArtists', artistes);
|
|
333
365
|
localStorage.setItem('ncs_currentCover', track.coverUrl);
|
|
334
366
|
localStorage.setItem('ncs_currentTime', 0);
|
|
335
367
|
this.savedTime = 0;
|
|
@@ -345,22 +377,19 @@ class NCSWidget {
|
|
|
345
377
|
|
|
346
378
|
async handleNext() {
|
|
347
379
|
const genre = this.genreSelect.value;
|
|
348
|
-
|
|
349
|
-
// Cas 1 : On a reculé dans l'historique et on veut revenir à la musique suivante connue
|
|
350
380
|
if (this.currentHistoryIndex < this.trackHistory.length - 1) {
|
|
351
381
|
this.currentHistoryIndex++;
|
|
352
382
|
this.setTrack(this.trackHistory[this.currentHistoryIndex], false);
|
|
353
383
|
this.prevBtn.disabled = this.currentHistoryIndex <= 0;
|
|
354
384
|
}
|
|
355
|
-
// Cas 2 : L'historique est au bout, on pioche dans la file d'attente (instantané !)
|
|
356
385
|
else if (this.nextTracksQueue.length > 0) {
|
|
357
|
-
const nextTrack = this.nextTracksQueue.shift();
|
|
386
|
+
const nextTrack = this.nextTracksQueue.shift();
|
|
358
387
|
this.setTrack(nextTrack, true);
|
|
359
|
-
this.fillQueue(genre);
|
|
388
|
+
this.fillQueue(genre);
|
|
360
389
|
}
|
|
361
|
-
// Cas 3 : L'utilisateur clique trop vite et la file d'attente est vide (Secours)
|
|
362
390
|
else {
|
|
363
391
|
this.trackName.innerText = "Recherche...";
|
|
392
|
+
this.artistsName.innerText = "...";
|
|
364
393
|
const track = await this.fetchSingleTrack(genre);
|
|
365
394
|
if (track) {
|
|
366
395
|
this.setTrack(track, true);
|
|
@@ -374,7 +403,6 @@ class NCSWidget {
|
|
|
374
403
|
this.currentHistoryIndex--;
|
|
375
404
|
this.setTrack(this.trackHistory[this.currentHistoryIndex], false);
|
|
376
405
|
this.prevBtn.disabled = this.currentHistoryIndex <= 0;
|
|
377
|
-
|
|
378
406
|
this.savedTime = 0;
|
|
379
407
|
localStorage.setItem('ncs_currentTime', 0);
|
|
380
408
|
}
|