@@ -2,7 +2,10 @@ function favouriteSection() {
22 const section = document . createElement ( "section" ) ;
33 section . classList . add ( "favourite-section" , "py-5" ) ;
44
5- // Data lagu dengan URL Hardcoded untuk performa maksimal di mobile
5+ // Data lagu dengan Spotify ID
6+ // CARA CARI ID: Buka Spotify > Klik 3 titik di lagu > Share > Copy Song Link
7+ // Ambil kode setelah "track/" sampai sebelum tanda tanya "?"
8+ // Contoh: https://open.spotify.com/track/4uUG5RXrOk84mYEfFvj3cK?si=... -> ID: 4uUG5RXrOk84mYEfFvj3cK
69 const songs = [
710 {
811 title : "Secukupnya" ,
@@ -12,8 +15,7 @@ function favouriteSection() {
1215 icon : "fa-music" ,
1316 color : "#ccff00" ,
1417 delay : 0 ,
15- previewUrl :
16- "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview116/v4/a6/5c/52/a65c529e-74a4-7b98-3e70-7f812a504a96/mzaf_1626184544281484870.plus.aac.p.m4a" ,
18+ spotifyId : "5drW6PGRxkE6MxttzVLNk5" , // Ganti dengan ID asli
1719 } ,
1820 {
1921 title : "Andai Saja" ,
@@ -23,8 +25,7 @@ function favouriteSection() {
2325 icon : "fa-magic" ,
2426 color : "#ffffff" ,
2527 delay : 100 ,
26- previewUrl :
27- "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview221/v4/34/a5/23/34a52368-643a-43a6-c96c-fd89c8d077cf/mzaf_14381344851997934839.plus.aac.p.m4a" ,
28+ spotifyId : "2hb8n44vpefszgLwkEVXhU" , // Ganti dengan ID asli
2829 } ,
2930 {
3031 title : "Kenangan Manis" ,
@@ -34,8 +35,7 @@ function favouriteSection() {
3435 icon : "fa-guitar" ,
3536 color : "#ccff00" ,
3637 delay : 200 ,
37- previewUrl :
38- "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview221/v4/09/89/1b/09891b19-1eb0-2f88-3339-1fad9861b6ed/mzaf_15403338971115180038.plus.aac.p.m4a" ,
38+ spotifyId : "5jLzpxUOnCsX5tfq7TGY9v" , // Ganti dengan ID asli
3939 } ,
4040 {
4141 title : "33x" ,
@@ -45,8 +45,7 @@ function favouriteSection() {
4545 icon : "fa-briefcase" ,
4646 color : "#ffffff" ,
4747 delay : 300 ,
48- previewUrl :
49- "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview126/v4/c6/be/d3/c6bed396-2b3c-0aee-dcec-d06a9506c781/mzaf_9218744999071382246.plus.aac.p.m4a" ,
48+ spotifyId : "0I3I9hgBRat6HR5Gzy26jx" , // Ganti dengan ID asli
5049 } ,
5150 {
5251 title : "Sedia Aku Sebelum Hujan" ,
@@ -56,8 +55,7 @@ function favouriteSection() {
5655 icon : "fa-cloud-rain" ,
5756 color : "#ccff00" ,
5857 delay : 400 ,
59- previewUrl :
60- "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview221/v4/9a/8a/f5/9a8af565-7b11-030b-6dc4-72a354411691/mzaf_1147188470658269281.plus.aac.p.m4a" ,
58+ spotifyId : "64qfw9JZl3RYCrq4Mn0WvT" , // Ganti dengan ID asli
6159 } ,
6260 {
6361 title : "Mesra-mesraanya kecil-kecilan dulu" ,
@@ -67,30 +65,41 @@ function favouriteSection() {
6765 icon : "fa-heart" ,
6866 color : "#ffffff" ,
6967 delay : 500 ,
70- previewUrl :
71- "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview126/v4/57/db/a8/57dba8ad-c886-2178-69bb-1ea1ca93380f/mzaf_13362817603498077160.plus.aac.p.m4a" ,
68+ spotifyId : "1NzyVlr2fOWOfigbwDabme" , // Ganti dengan ID asli
7269 } ,
7370 ] ;
7471
75- // Helper untuk bikin item lagu
72+ // Helper untuk bikin item lagu dengan Spotify Embed
7673 const createSongItem = ( song , index ) => `
7774 <div class="col-lg-6" data-aos="fade-up" data-aos-delay="${ song . delay } ">
78- <div class="song-neo">
79- <div class="song-icon" style="background: ${ song . color } ">
80- <i class="fas ${ song . icon } "></i>
81- </div>
82- <div class="song-details">
83- <div class="d-flex justify-content-between align-items-start">
84- <div>
85- <h4 class="mb-0 song-title">${ song . title } </h4>
86- <div class="artist-badge song-artist">${ song . artist } </div>
75+ <div class="song-neo h-100">
76+ <div class="song-details d-flex flex-column h-100">
77+ <div class="d-flex justify-content-between align-items-start mb-3">
78+ <div class="d-flex align-items-center">
79+ <div class="song-icon-small me-3" style="background: ${ song . color } ">
80+ <i class="fas ${ song . icon } "></i>
81+ </div>
82+ <div>
83+ <h4 class="mb-0 song-title">${ song . title } </h4>
84+ <div class="artist-badge song-artist">${ song . artist } </div>
85+ </div>
8786 </div>
88- <button class="btn btn-sm btn-icon play-btn-neo" data-index="${ index } ">
89- <i class="fas fa-play"></i>
90- </button>
9187 </div>
92- <p class="album-text mt-2 mb-2"><i class="fas fa-compact-disc me-1"></i> ${ song . album } </p>
93- <p class="desc-text">${ song . desc } </p>
88+
89+ <p class="desc-text mb-3 flex-grow-1">${ song . desc } </p>
90+
91+ <!-- Spotify Embed -->
92+ <div class="spotify-embed-wrapper">
93+ <iframe style="border-radius:12px"
94+ src="https://open.spotify.com/embed/track/${ song . spotifyId } ?utm_source=generator&theme=0"
95+ width="100%"
96+ height="80"
97+ frameBorder="0"
98+ allowfullscreen=""
99+ allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
100+ loading="lazy">
101+ </iframe>
102+ </div>
94103 </div>
95104 </div>
96105 </div>
@@ -126,31 +135,29 @@ function favouriteSection() {
126135 background: var(--neo-white);
127136 border: 3px solid var(--neo-black);
128137 box-shadow: 5px 5px 0px rgba(0,0,0,0.2);
129- display: flex;
130- align-items: stretch;
131- height: 100%;
132138 transition: transform 0.2s;
139+ overflow: hidden;
133140 }
134141
135142 .song-neo:hover {
136143 transform: translateX(-5px);
137144 box-shadow: 8px 8px 0px var(--neo-black);
138145 }
139146
140- .song-icon {
141- width: 100px;
142- border-right: 3px solid var(--neo-black);
147+ .song-icon-small {
148+ width: 50px;
149+ height: 50px;
150+ border: 2px solid var(--neo-black);
143151 display: flex;
144152 align-items: center;
145153 justify-content: center;
146- font-size: 2.5rem ;
154+ font-size: 1.2rem ;
147155 color: var(--neo-black);
148156 flex-shrink: 0;
149157 }
150158
151159 .song-details {
152- padding: 1.2rem;
153- flex-grow: 1;
160+ padding: 1.5rem;
154161 }
155162
156163 .artist-badge {
@@ -164,132 +171,21 @@ function favouriteSection() {
164171 text-transform: uppercase;
165172 }
166173
167- .album-text {
168- font-size: 0.85rem;
169- color: #666;
170- border-bottom: 2px dashed #ccc;
171- padding-bottom: 5px;
172- }
173-
174174 .desc-text {
175- font-size: 0.9rem ;
176- line-height: 1.4 ;
177- margin-bottom: 0 ;
175+ font-size: 0.95rem ;
176+ line-height: 1.5 ;
177+ color: #333 ;
178178 }
179179
180- .btn-icon {
180+ .spotify-embed-wrapper {
181+ margin-top: auto;
181182 border: 2px solid var(--neo-black);
182- border-radius: 50%;
183- width: 35px; height: 35px;
184- display: flex; align-items: center; justify-content: center;
185- }
186-
187- .btn-icon:hover {
188- background: var(--neo-green);
189- }
190-
191- .btn-black {
192- background: var(--neo-black);
193- color: var(--neo-white);
194- border-radius: 0;
195- font-weight: bold;
196- }
197- .btn-black:hover {
198- background: #333;
199- color: var(--neo-green);
183+ border-radius: 14px; /* Sedikit lebih besar dari iframe radius */
184+ background: #000;
200185 }
201186 </style>
202187 ` ;
203188
204- // Audio Logic - Single Instance untuk Mobile Stability
205- const audioPlayer = new Audio ( ) ;
206- let currentPlayingIndex = - 1 ; // Track index lagu yang sedang diputar
207-
208- // Reset UI saat lagu selesai
209- audioPlayer . onended = ( ) => {
210- const activeBtn = section . querySelector (
211- `.play-btn-neo[data-index="${ currentPlayingIndex } "]`
212- ) ;
213- if ( activeBtn ) {
214- const icon = activeBtn . querySelector ( "i" ) ;
215- icon . classList . remove ( "fa-pause" ) ;
216- icon . classList . add ( "fa-play" ) ;
217- }
218- currentPlayingIndex = - 1 ;
219- } ;
220-
221- // Handle Error Playback
222- audioPlayer . onerror = ( e ) => {
223- console . error ( "Audio Error:" , e ) ;
224- alert ( "Gagal memutar audio. Koneksi internet mungkin tidak stabil." ) ;
225- currentPlayingIndex = - 1 ;
226-
227- // Reset semua icon loading
228- section . querySelectorAll ( ".fa-spinner" ) . forEach ( ( icon ) => {
229- icon . classList . remove ( "fa-spinner" , "fa-spin" ) ;
230- icon . classList . add ( "fa-play" ) ;
231- } ) ;
232- } ;
233-
234- section . querySelectorAll ( ".play-btn-neo" ) . forEach ( ( btn ) => {
235- btn . addEventListener ( "click" , async function ( ) {
236- const icon = this . querySelector ( "i" ) ;
237- const index = parseInt ( this . getAttribute ( "data-index" ) ) ;
238- const song = songs [ index ] ;
239-
240- // 1. Logika Pause: Jika klik tombol yang sama dan sedang main
241- if ( currentPlayingIndex === index && ! audioPlayer . paused ) {
242- audioPlayer . pause ( ) ;
243- icon . classList . remove ( "fa-pause" ) ;
244- icon . classList . add ( "fa-play" ) ;
245- return ;
246- }
247-
248- // 2. Logika Ganti Lagu: Reset tombol lagu sebelumnya
249- if ( currentPlayingIndex !== - 1 && currentPlayingIndex !== index ) {
250- const prevBtn = section . querySelector (
251- `.play-btn-neo[data-index="${ currentPlayingIndex } "]`
252- ) ;
253- if ( prevBtn ) {
254- const prevIcon = prevBtn . querySelector ( "i" ) ;
255- prevIcon . classList . remove ( "fa-pause" , "fa-spinner" , "fa-spin" ) ;
256- prevIcon . classList . add ( "fa-play" ) ;
257- }
258- }
259-
260- // 3. Logika Play
261- // Set loading state
262- icon . classList . remove ( "fa-play" ) ;
263- icon . classList . add ( "fa-spinner" , "fa-spin" ) ;
264-
265- try {
266- // Set source langsung (tanpa fetch)
267- audioPlayer . src = song . previewUrl ;
268- currentPlayingIndex = index ;
269-
270- // Promise play() untuk handle autoplay policy
271- await audioPlayer . play ( ) ;
272-
273- // Jika berhasil play
274- icon . classList . remove ( "fa-spinner" , "fa-spin" ) ;
275- icon . classList . add ( "fa-pause" ) ;
276- } catch ( error ) {
277- console . error ( "Playback failed:" , error ) ;
278- icon . classList . remove ( "fa-spinner" , "fa-spin" ) ;
279- icon . classList . add ( "fa-play" ) ;
280-
281- if ( error . name === "NotAllowedError" ) {
282- alert (
283- "Browser memblokir audio. Silakan klik tombol play sekali lagi."
284- ) ;
285- } else {
286- alert ( "Gagal memutar lagu: " + error . message ) ;
287- }
288- currentPlayingIndex = - 1 ;
289- }
290- } ) ;
291- } ) ;
292-
293189 return section ;
294190}
295191
0 commit comments