nocopyrightsounds-widget 1.0.7 → 1.1.1
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 +123 -10
- package/package.json +10 -3
- package/src/index.js +84 -102
package/README.md
CHANGED
|
@@ -1,20 +1,133 @@
|
|
|
1
|
-
# NoCopyrightSounds Widget
|
|
1
|
+
# 🎧 NoCopyrightSounds (NCS) Web Widget
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/nocopyrightsounds-widget)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Un lecteur musical flottant, élégant et hautement personnalisable pour intégrer facilement les musiques libres de droits de **NoCopyrightSounds** à n'importe quel site web.
|
|
6
7
|
|
|
8
|
+
Conçu pour les développeurs modernes : léger, persistant entre les changements de pages, et entièrement paramétrable via JavaScript et CSS.
|
|
9
|
+
|
|
10
|
+
 *(Ajoutez une capture d'écran de votre widget dans votre dépôt GitHub et remplacez ce lien plus tard !)*
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ✨ Fonctionnalités
|
|
15
|
+
|
|
16
|
+
* ⚡ **Zéro Latence :** Algorithme de préchargement (buffering) intelligent en arrière-plan pour des transitions instantanées entre les morceaux.
|
|
17
|
+
* 💾 **Persistance d'état :** Mémorise la piste en cours, le volume, la progression et l'état d'ouverture du widget d'une page à l'autre via `localStorage`.
|
|
18
|
+
* 🎨 **Thèmes & Couleurs :** Support natif des modes clair (`light`) et sombre (`dark`), avec personnalisation de la couleur principale.
|
|
19
|
+
* 🎛️ **Contrôles Complets :** Boutons Suivant/Précédent avec historique, contrôle du volume, Mute, et barre de progression cliquable.
|
|
20
|
+
* 🎵 **+60 Genres :** Navigation aléatoire intelligente parmi tout le catalogue historique de NCS (House, Dubstep, Chill, etc.).
|
|
21
|
+
* ⬇️ **Téléchargement :** Bouton intégré pour récupérer directement le fichier MP3 officiel.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📦 Installation
|
|
26
|
+
|
|
27
|
+
### Via NPM (Recommandé pour React, Vue, Angular...)
|
|
7
28
|
\`\`\`bash
|
|
8
29
|
npm install nocopyrightsounds-widget
|
|
9
30
|
\`\`\`
|
|
10
31
|
|
|
11
|
-
|
|
32
|
+
### Via CDN (Pour les sites HTML classiques / Vanilla JS)
|
|
33
|
+
\`\`\`html
|
|
34
|
+
<script type="module">
|
|
35
|
+
import NCSWidget from 'https://cdn.jsdelivr.net/npm/nocopyrightsounds-widget@latest/src/index.js';
|
|
36
|
+
</script>
|
|
37
|
+
\`\`\`
|
|
38
|
+
|
|
39
|
+
---
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
## 🚀 Utilisation Rapide
|
|
42
|
+
|
|
43
|
+
### Exemple en Vanilla JS (HTML)
|
|
44
|
+
\`\`\`html
|
|
45
|
+
<body>
|
|
46
|
+
<script type="module">
|
|
47
|
+
import NCSWidget from 'https://cdn.jsdelivr.net/npm/nocopyrightsounds-widget@latest/src/index.js';
|
|
48
|
+
|
|
49
|
+
// Initialisation basique
|
|
50
|
+
const player = new NCSWidget();
|
|
51
|
+
</script>
|
|
52
|
+
</body>
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
### Exemple dans React (Next.js, Vite...)
|
|
56
|
+
\`\`\`jsx
|
|
57
|
+
import { useEffect } from 'react';
|
|
14
58
|
import NCSWidget from 'nocopyrightsounds-widget';
|
|
15
59
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
60
|
+
export default function App() {
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const widget = new NCSWidget({
|
|
63
|
+
position: 'bottom-right',
|
|
64
|
+
theme: 'dark',
|
|
65
|
+
primaryColor: '#1DB954'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Nettoyage lors du démontage du composant
|
|
69
|
+
return () => {
|
|
70
|
+
const el = document.getElementById('ncs-persistent-widget');
|
|
71
|
+
if (el) el.remove();
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<div>
|
|
77
|
+
<h1>Mon Super Site</h1>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## ⚙️ Configuration (Options de l'objet)
|
|
86
|
+
|
|
87
|
+
Vous pouvez passer un objet d'options au constructeur pour personnaliser le comportement du widget :
|
|
88
|
+
|
|
89
|
+
| Option | Type | Défaut | Description |
|
|
90
|
+
| :--- | :--- | :--- | :--- |
|
|
91
|
+
| \`position\` | String | \`'bottom-right'\` | Position à l'écran (\`bottom-right\`, \`bottom-left\`, \`top-right\`, \`top-left\`). |
|
|
92
|
+
| \`offset\` | String | \`'25px'\` | Marge par rapport au bord de l'écran. |
|
|
93
|
+
| \`theme\` | String | \`'dark'\` | Thème de base de l'interface (\`'dark'\` ou \`'light'\`). |
|
|
94
|
+
| \`primaryColor\` | String | \`'#1DB954'\` | Couleur principale (Bouton d'ouverture, slider, visualizer). |
|
|
95
|
+
| \`defaultGenre\` | String | \`'all'\` | L'ID du genre au démarrage (ex: \`'10'\` pour House). |
|
|
96
|
+
| \`startVolume\` | Number | \`0.5\` | Volume initial entre 0.0 et 1.0 (surchargé si l'utilisateur a déjà un cache). |
|
|
97
|
+
| \`zIndex\` | Number | \`99999\` | Profondeur d'affichage CSS (z-index). |
|
|
98
|
+
| \`apiUrl\` | String | *https://www.wordreference.com/definition/interne* | URL de l'API Backend. Vous pouvez héberger la vôtre si besoin. |
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 🎨 Personnalisation CSS Avancée
|
|
103
|
+
|
|
104
|
+
Si les options du constructeur ne suffisent pas, le widget expose des **Variables CSS** (Custom Properties) rattachées à l'ID `#ncs-persistent-widget`. Vous pouvez les surcharger directement dans la feuille de style de votre site :
|
|
105
|
+
|
|
106
|
+
\`\`\`css
|
|
107
|
+
/* Dans le fichier style.css de votre site web */
|
|
108
|
+
#ncs-persistent-widget {
|
|
109
|
+
--ncs-bg: #000000; /* Fond du widget (Noir pur) */
|
|
110
|
+
--ncs-border: #333333; /* Couleur de la bordure */
|
|
111
|
+
--ncs-primary: #ff0055; /* Remplace le vert par du rose fluo */
|
|
112
|
+
--ncs-panel-bg: #111111; /* Fond des éléments internes (images, selects) */
|
|
113
|
+
font-family: 'Roboto', sans-serif; /* Changement de police */
|
|
114
|
+
border-radius: 0px; /* Retirer les coins arrondis */
|
|
115
|
+
}
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 🏗️ Architecture & Backend
|
|
121
|
+
|
|
122
|
+
En raison des restrictions CORS strictes sur le web moderne, un navigateur web ne peut pas interroger directement le site de NCS.
|
|
123
|
+
Ce widget s'appuie donc sur une API Backend Node.js qui sert de relais de données (Proxy).
|
|
124
|
+
|
|
125
|
+
*Note : Une API publique par défaut est fournie avec ce widget pour un usage immédiat. Pour des environnements de production à fort trafic, il est recommandé de déployer votre propre instance du serveur relais.*
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 📄 Licence
|
|
130
|
+
|
|
131
|
+
Distribué sous la licence MIT. Voir `LICENSE` pour plus d'informations.
|
|
132
|
+
|
|
133
|
+
**Avertissement :** Ce projet n'est pas affilié à NoCopyrightSounds. Toutes les musiques diffusées par ce widget appartiennent à leurs créateurs respectifs et à NCS. Veuillez respecter les conditions d'utilisation de NoCopyrightSounds lors de l'utilisation de leurs œuvres.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nocopyrightsounds-widget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Un widget musical persistant pour site web utilisant l'API NoCopyrightSounds",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/floriangobin/nocopyrightsounds-widget.git"
|
|
13
13
|
},
|
|
14
|
-
"keywords": [
|
|
14
|
+
"keywords": [
|
|
15
|
+
"ncs",
|
|
16
|
+
"music",
|
|
17
|
+
"widget",
|
|
18
|
+
"audio",
|
|
19
|
+
"player",
|
|
20
|
+
"nocopyrightsounds"
|
|
21
|
+
],
|
|
15
22
|
"author": "GOBIN Florian",
|
|
16
23
|
"license": "MIT"
|
|
17
|
-
}
|
|
24
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
class NCSWidget {
|
|
2
|
-
constructor(
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
constructor(userOptions = {}) {
|
|
3
|
+
// 🛠️ 1. Configurations par défaut (Fusionnées avec les choix de l'utilisateur)
|
|
4
|
+
const defaultOptions = {
|
|
5
|
+
position: 'bottom-right',
|
|
6
|
+
apiUrl: 'https://VOTRE-URL-RENDER.onrender.com', // ⚠️ METTEZ VOTRE URL RENDER ICI
|
|
7
|
+
theme: 'dark', // 'dark' ou 'light'
|
|
8
|
+
primaryColor: '#1DB954', // Vert NCS par défaut
|
|
9
|
+
defaultGenre: 'all',
|
|
10
|
+
startVolume: 0.5,
|
|
11
|
+
offset: '25px', // Distance par rapport au bord de l'écran
|
|
12
|
+
zIndex: 99999
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
this.options = { ...defaultOptions, ...userOptions };
|
|
5
16
|
|
|
6
17
|
this.audio = new Audio();
|
|
7
18
|
this.isPlaying = false;
|
|
@@ -13,10 +24,9 @@ class NCSWidget {
|
|
|
13
24
|
|
|
14
25
|
this.isBrowser = typeof window !== 'undefined';
|
|
15
26
|
if (this.isBrowser) {
|
|
16
|
-
// Le volume est à 1 (100%) par défaut s'il n'y a rien en cache
|
|
17
27
|
const savedVol = localStorage.getItem('ncs_volume');
|
|
18
|
-
this.audio.volume = savedVol !== null ? parseFloat(savedVol) :
|
|
19
|
-
this.lastVolume = this.audio.volume > 0 ? this.audio.volume :
|
|
28
|
+
this.audio.volume = savedVol !== null ? parseFloat(savedVol) : this.options.startVolume;
|
|
29
|
+
this.lastVolume = this.audio.volume > 0 ? this.audio.volume : this.options.startVolume;
|
|
20
30
|
|
|
21
31
|
this.savedTime = localStorage.getItem('ncs_currentTime') || 0;
|
|
22
32
|
this.savedTrack = localStorage.getItem('ncs_currentTrack') || null;
|
|
@@ -33,7 +43,9 @@ class NCSWidget {
|
|
|
33
43
|
this.restoreTrack();
|
|
34
44
|
this.fillQueue(this.genreSelect.value);
|
|
35
45
|
} else {
|
|
36
|
-
|
|
46
|
+
// Utiliser le genre par défaut défini dans les options
|
|
47
|
+
this.genreSelect.value = this.options.defaultGenre;
|
|
48
|
+
this.changeGenre(this.options.defaultGenre);
|
|
37
49
|
}
|
|
38
50
|
}
|
|
39
51
|
|
|
@@ -43,54 +55,85 @@ class NCSWidget {
|
|
|
43
55
|
this.container = document.createElement('div');
|
|
44
56
|
this.container.id = 'ncs-persistent-widget';
|
|
45
57
|
|
|
58
|
+
// 🎨 2. Application du thème (Variables CSS dynamiques)
|
|
59
|
+
const isLight = this.options.theme === 'light';
|
|
60
|
+
const colors = {
|
|
61
|
+
bg: isLight ? '#ffffff' : '#181818',
|
|
62
|
+
text: isLight ? '#222222' : '#ffffff',
|
|
63
|
+
textMuted: isLight ? '#666666' : '#b3b3b3',
|
|
64
|
+
border: isLight ? '#e0e0e0' : '#282828',
|
|
65
|
+
panelBg: isLight ? '#f5f5f5' : '#282828',
|
|
66
|
+
sliderBg: isLight ? '#d3d3d3' : '#535353',
|
|
67
|
+
btnBg: isLight ? '#222222' : '#ffffff',
|
|
68
|
+
btnColor: isLight ? '#ffffff' : '#000000'
|
|
69
|
+
};
|
|
70
|
+
|
|
46
71
|
const style = document.createElement('style');
|
|
47
72
|
style.textContent = `
|
|
48
|
-
#ncs-persistent-widget {
|
|
49
|
-
|
|
73
|
+
#ncs-persistent-widget {
|
|
74
|
+
/* Variables CSS exposées pour les développeurs */
|
|
75
|
+
--ncs-primary: ${this.options.primaryColor};
|
|
76
|
+
--ncs-bg: ${colors.bg};
|
|
77
|
+
--ncs-text: ${colors.text};
|
|
78
|
+
--ncs-text-muted: ${colors.textMuted};
|
|
79
|
+
--ncs-border: ${colors.border};
|
|
80
|
+
--ncs-panel-bg: ${colors.panelBg};
|
|
81
|
+
--ncs-slider-bg: ${colors.sliderBg};
|
|
82
|
+
--ncs-btn-bg: ${colors.btnBg};
|
|
83
|
+
--ncs-btn-color: ${colors.btnColor};
|
|
84
|
+
|
|
85
|
+
position: fixed;
|
|
86
|
+
${this.getPositionStyles()}
|
|
87
|
+
z-index: ${this.options.zIndex};
|
|
88
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
89
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.ncs-minimized { width: 55px; height: 55px; border-radius: 50%; background: var(--ncs-primary); cursor: pointer; display: flex; justify-content: center; align-items: center; box-shadow: 0 6px 15px rgba(0,0,0, 0.2); font-size: 24px; transition: transform 0.2s; color: white; }
|
|
50
93
|
.ncs-minimized:hover { transform: scale(1.1); }
|
|
51
94
|
.ncs-minimized.hidden { display: none; }
|
|
52
95
|
|
|
53
|
-
.ncs-expanded { width: 320px; background:
|
|
96
|
+
.ncs-expanded { width: 320px; background: var(--ncs-bg); color: var(--ncs-text); border-radius: 16px; padding: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); display: none; border: 1px solid var(--ncs-border); }
|
|
54
97
|
.ncs-expanded.active { display: block; animation: ncsFadeIn 0.3s ease; }
|
|
55
98
|
|
|
56
99
|
.ncs-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
|
|
57
|
-
.ncs-header strong { font-size: 14px; font-weight: 600; color:
|
|
58
|
-
.ncs-close-btn { background: transparent; border: none; color:
|
|
59
|
-
.ncs-close-btn:hover { color:
|
|
100
|
+
.ncs-header strong { font-size: 14px; font-weight: 600; color: var(--ncs-text-muted); letter-spacing: 1px; text-transform: uppercase; display: flex; align-items: center; gap: 8px; }
|
|
101
|
+
.ncs-close-btn { background: transparent; border: none; color: var(--ncs-text-muted); font-size: 18px; cursor: pointer; padding: 0; transition: color 0.2s; }
|
|
102
|
+
.ncs-close-btn:hover { color: var(--ncs-text); }
|
|
60
103
|
|
|
61
104
|
.ncs-visualizer { display: flex; gap: 2px; height: 12px; align-items: flex-end; opacity: 0; transition: opacity 0.3s; }
|
|
62
105
|
.ncs-visualizer.playing { opacity: 1; }
|
|
63
|
-
.ncs-bar { width: 3px; background:
|
|
106
|
+
.ncs-bar { width: 3px; background: var(--ncs-primary); border-radius: 2px; animation: bounce 0.5s infinite alternate; }
|
|
64
107
|
.ncs-bar:nth-child(2) { animation-delay: 0.15s; }
|
|
65
108
|
.ncs-bar:nth-child(3) { animation-delay: 0.3s; }
|
|
66
109
|
@keyframes bounce { from { height: 3px; } to { height: 12px; } }
|
|
67
110
|
|
|
68
111
|
.ncs-track-info { display: flex; align-items: center; margin-bottom: 15px; }
|
|
69
|
-
.ncs-cover { width: 65px; height: 65px; border-radius: 8px; background:
|
|
112
|
+
.ncs-cover { width: 65px; height: 65px; border-radius: 8px; background: var(--ncs-panel-bg); margin-right: 15px; object-fit: cover; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }
|
|
70
113
|
.ncs-details { flex: 1; overflow: hidden; display: flex; flex-direction: column; justify-content: center; }
|
|
71
|
-
#ncs-track-name { font-size: 14px; font-weight:
|
|
72
|
-
#ncs-artists { font-size: 11px; color:
|
|
73
|
-
#ncs-genre { width: 100%; padding: 4px 8px; background:
|
|
114
|
+
#ncs-track-name { font-size: 14px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 2px; }
|
|
115
|
+
#ncs-artists { font-size: 11px; color: var(--ncs-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 8px; }
|
|
116
|
+
#ncs-genre { width: 100%; padding: 4px 8px; background: var(--ncs-panel-bg); color: var(--ncs-text); border: 1px solid var(--ncs-border); border-radius: 6px; font-size: 12px; cursor: pointer; outline: none; }
|
|
74
117
|
|
|
75
|
-
.ncs-progress-container { margin-bottom: 15px; display:flex; align-items:center; gap: 10px; font-size: 11px; color:
|
|
76
|
-
.ncs-slider { -webkit-appearance: none; width: 100%; height: 4px; background:
|
|
77
|
-
.ncs-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; background:
|
|
118
|
+
.ncs-progress-container { margin-bottom: 15px; display:flex; align-items:center; gap: 10px; font-size: 11px; color: var(--ncs-text-muted); font-variant-numeric: tabular-nums; }
|
|
119
|
+
.ncs-slider { -webkit-appearance: none; width: 100%; height: 4px; background: var(--ncs-slider-bg); border-radius: 2px; outline: none; cursor: pointer; }
|
|
120
|
+
.ncs-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%; background: var(--ncs-primary); cursor: pointer; transition: transform 0.1s; }
|
|
78
121
|
.ncs-slider::-webkit-slider-thumb:hover { transform: scale(1.2); }
|
|
79
122
|
|
|
80
123
|
.ncs-controls { display: flex; justify-content: center; align-items: center; gap: 15px; margin-bottom: 15px; }
|
|
81
|
-
.ncs-btn-circle { width: 50px; height: 50px; border-radius: 50%; background:
|
|
124
|
+
.ncs-btn-circle { width: 50px; height: 50px; border-radius: 50%; background: var(--ncs-btn-bg); color: var(--ncs-btn-color); border: none; font-size: 20px; cursor: pointer; display:flex; justify-content:center; align-items:center; transition: transform 0.2s; padding-left: 4px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
|
|
82
125
|
.ncs-btn-circle.paused { padding-left: 0; }
|
|
83
126
|
.ncs-btn-circle:hover { transform: scale(1.05); }
|
|
84
|
-
.ncs-btn-icon { background: transparent; border: none; color:
|
|
85
|
-
.ncs-btn-icon:hover { color:
|
|
86
|
-
.ncs-btn-icon:disabled { color:
|
|
127
|
+
.ncs-btn-icon { background: transparent; border: none; color: var(--ncs-text-muted); font-size: 20px; cursor: pointer; transition: color 0.2s; padding: 5px; }
|
|
128
|
+
.ncs-btn-icon:hover { color: var(--ncs-text); }
|
|
129
|
+
.ncs-btn-icon:disabled { color: var(--ncs-border); cursor: not-allowed; }
|
|
87
130
|
|
|
88
131
|
.ncs-bottom-bar { display: flex; justify-content: space-between; align-items: center; }
|
|
89
|
-
.ncs-volume-container { display: flex; align-items: center; gap: 8px; color:
|
|
132
|
+
.ncs-volume-container { display: flex; align-items: center; gap: 8px; color: var(--ncs-text-muted); flex: 1; margin-right: 15px; }
|
|
90
133
|
#ncs-mute-btn { cursor: pointer; transition: transform 0.1s; user-select: none; }
|
|
91
134
|
#ncs-mute-btn:hover { transform: scale(1.1); }
|
|
92
|
-
.ncs-download-btn { color:
|
|
93
|
-
.ncs-download-btn:hover { color:
|
|
135
|
+
.ncs-download-btn { color: var(--ncs-text-muted); text-decoration: none; font-size: 18px; transition: color 0.2s; }
|
|
136
|
+
.ncs-download-btn:hover { color: var(--ncs-primary); }
|
|
94
137
|
|
|
95
138
|
@keyframes ncsFadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
|
96
139
|
`;
|
|
@@ -115,72 +158,14 @@ class NCSWidget {
|
|
|
115
158
|
<select id="ncs-genre">
|
|
116
159
|
<option value="all">🌍 Tous les genres</option>
|
|
117
160
|
<option value="31">Alternative Dance</option>
|
|
118
|
-
<option value="
|
|
119
|
-
<option value="33">Alternative Pop</option>
|
|
120
|
-
<option value="23">Ambient</option>
|
|
121
|
-
<option value="34">Anti-Pop</option>
|
|
122
|
-
<option value="1">Bass</option>
|
|
123
|
-
<option value="18">Bass House</option>
|
|
124
|
-
<option value="78">Bass Music</option>
|
|
125
|
-
<option value="26">Brazilian Phonk</option>
|
|
126
|
-
<option value="27">Breakbeat</option>
|
|
161
|
+
<option value="10">House</option>
|
|
127
162
|
<option value="2">Chill</option>
|
|
128
|
-
<option value="24">Chill Bass</option>
|
|
129
|
-
<option value="35">Chill Pop</option>
|
|
130
|
-
<option value="85">Colour Bass</option>
|
|
131
|
-
<option value="65">Complextro</option>
|
|
132
|
-
<option value="36">Dance-Pop</option>
|
|
133
|
-
<option value="66">Deep House</option>
|
|
134
|
-
<option value="45">Disco</option>
|
|
135
|
-
<option value="46">Disco House</option>
|
|
136
|
-
<option value="3">Drum & Bass</option>
|
|
137
|
-
<option value="4">Drumstep</option>
|
|
138
163
|
<option value="5">Dubstep</option>
|
|
139
|
-
<option value="6">EDM</option>
|
|
140
|
-
<option value="47">Electro</option>
|
|
141
|
-
<option value="48">Electro House</option>
|
|
142
164
|
<option value="7">Electronic</option>
|
|
143
|
-
<option value="
|
|
144
|
-
<option value="83">Electronic Rock</option>
|
|
145
|
-
<option value="17">Future Bass</option>
|
|
146
|
-
<option value="68">Future Bounce</option>
|
|
147
|
-
<option value="50">Future Funk</option>
|
|
148
|
-
<option value="8">Future House</option>
|
|
149
|
-
<option value="69">Future Rave</option>
|
|
150
|
-
<option value="57">Future Trap</option>
|
|
151
|
-
<option value="40">Futurepop</option>
|
|
152
|
-
<option value="51">Garage</option>
|
|
153
|
-
<option value="15">Glitch Hop</option>
|
|
154
|
-
<option value="82">Hardcore</option>
|
|
165
|
+
<option value="3">Drum & Bass</option>
|
|
155
166
|
<option value="9">Hardstyle</option>
|
|
156
|
-
<option value="10">House</option>
|
|
157
|
-
<option value="41">Hyperpop</option>
|
|
158
|
-
<option value="11">Indie Dance</option>
|
|
159
|
-
<option value="91">J-Pop</option>
|
|
160
|
-
<option value="84">Jersey Club</option>
|
|
161
|
-
<option value="28">Jump-Up</option>
|
|
162
|
-
<option value="29">Liquid DnB</option>
|
|
163
|
-
<option value="60">Lofi Hip-Hop</option>
|
|
164
|
-
<option value="12">Melodic Dubstep</option>
|
|
165
|
-
<option value="54">Melodic House</option>
|
|
166
|
-
<option value="22">Midtempo Bass</option>
|
|
167
|
-
<option value="30">Neurofunk</option>
|
|
168
|
-
<option value="87">Nu-Jazz</option>
|
|
169
|
-
<option value="16">Phonk</option>
|
|
170
|
-
<option value="86">Pluggnb</option>
|
|
171
|
-
<option value="19">Pop</option>
|
|
172
|
-
<option value="55">Progressive House</option>
|
|
173
|
-
<option value="88">RnB</option>
|
|
174
|
-
<option value="89">Speed Garage</option>
|
|
175
|
-
<option value="73">Tech House</option>
|
|
176
|
-
<option value="80">Techno</option>
|
|
177
|
-
<option value="81">Trance</option>
|
|
178
167
|
<option value="14">Trap</option>
|
|
179
|
-
|
|
180
|
-
<option value="21">UKG</option>
|
|
181
|
-
<option value="92">Wave</option>
|
|
182
|
-
<option value="90">Witch House</option>
|
|
183
|
-
</select>
|
|
168
|
+
</select>
|
|
184
169
|
</div>
|
|
185
170
|
</div>
|
|
186
171
|
|
|
@@ -209,6 +194,7 @@ class NCSWidget {
|
|
|
209
194
|
document.head.appendChild(style);
|
|
210
195
|
document.body.appendChild(this.container);
|
|
211
196
|
|
|
197
|
+
// Références
|
|
212
198
|
this.minimized = this.container.querySelector('.ncs-minimized');
|
|
213
199
|
this.expanded = this.container.querySelector('.ncs-expanded');
|
|
214
200
|
this.playBtn = this.container.querySelector('#ncs-play-pause');
|
|
@@ -228,13 +214,14 @@ class NCSWidget {
|
|
|
228
214
|
}
|
|
229
215
|
|
|
230
216
|
getPositionStyles() {
|
|
217
|
+
const offset = this.options.offset;
|
|
231
218
|
const positions = {
|
|
232
|
-
'bottom-right':
|
|
233
|
-
'bottom-left':
|
|
234
|
-
'top-right':
|
|
235
|
-
'top-left':
|
|
219
|
+
'bottom-right': `bottom: ${offset}; right: ${offset};`,
|
|
220
|
+
'bottom-left': `bottom: ${offset}; left: ${offset};`,
|
|
221
|
+
'top-right': `top: ${offset}; right: ${offset};`,
|
|
222
|
+
'top-left': `top: ${offset}; left: ${offset};`
|
|
236
223
|
};
|
|
237
|
-
return positions[this.position] || positions['bottom-right'];
|
|
224
|
+
return positions[this.options.position] || positions['bottom-right'];
|
|
238
225
|
}
|
|
239
226
|
|
|
240
227
|
attachEvents() {
|
|
@@ -267,30 +254,26 @@ class NCSWidget {
|
|
|
267
254
|
|
|
268
255
|
this.progressBar.addEventListener('input', (e) => { this.audio.currentTime = e.target.value; });
|
|
269
256
|
|
|
270
|
-
// --- NOUVEAU : GESTION DU MUTE ET DU VOLUME ---
|
|
271
257
|
this.volumeBar.addEventListener('input', (e) => {
|
|
272
258
|
const vol = parseFloat(e.target.value);
|
|
273
259
|
this.audio.volume = vol;
|
|
274
|
-
if (vol > 0) this.lastVolume = vol;
|
|
260
|
+
if (vol > 0) this.lastVolume = vol;
|
|
275
261
|
this.updateMuteIcon(vol);
|
|
276
262
|
localStorage.setItem('ncs_volume', vol);
|
|
277
263
|
});
|
|
278
264
|
|
|
279
265
|
this.muteBtn.addEventListener('click', () => {
|
|
280
266
|
if (this.audio.volume > 0) {
|
|
281
|
-
// On Mute
|
|
282
267
|
this.lastVolume = this.audio.volume;
|
|
283
268
|
this.audio.volume = 0;
|
|
284
269
|
this.volumeBar.value = 0;
|
|
285
270
|
} else {
|
|
286
|
-
// On Unmute
|
|
287
271
|
this.audio.volume = this.lastVolume || 1.0;
|
|
288
272
|
this.volumeBar.value = this.audio.volume;
|
|
289
273
|
}
|
|
290
274
|
this.updateMuteIcon(this.audio.volume);
|
|
291
275
|
localStorage.setItem('ncs_volume', this.audio.volume);
|
|
292
276
|
});
|
|
293
|
-
// ----------------------------------------------
|
|
294
277
|
|
|
295
278
|
setInterval(() => {
|
|
296
279
|
if (this.isPlaying && this.audio.currentTime > 0) {
|
|
@@ -346,7 +329,7 @@ class NCSWidget {
|
|
|
346
329
|
|
|
347
330
|
async fetchSingleTrack(genre) {
|
|
348
331
|
try {
|
|
349
|
-
const response = await fetch(`${this.apiUrl}/search?genre=${genre}`);
|
|
332
|
+
const response = await fetch(`${this.options.apiUrl}/search?genre=${genre}`);
|
|
350
333
|
const data = await response.json();
|
|
351
334
|
return (data && data.length > 0) ? data[0] : null;
|
|
352
335
|
} catch (error) {
|
|
@@ -406,10 +389,9 @@ class NCSWidget {
|
|
|
406
389
|
this.audio.src = track.audioUrl;
|
|
407
390
|
this.trackName.innerText = track.title;
|
|
408
391
|
|
|
409
|
-
// Mettre à jour l'affichage des artistes
|
|
410
392
|
const artistes = track.artists || "NCS Release";
|
|
411
393
|
this.artistsName.innerText = artistes;
|
|
412
|
-
this.artistsName.title = artistes;
|
|
394
|
+
this.artistsName.title = artistes;
|
|
413
395
|
|
|
414
396
|
if (track.coverUrl) this.coverImg.src = track.coverUrl;
|
|
415
397
|
this.downloadBtn.href = track.audioUrl;
|