Spaces:
Paused
Paused
| const axios = require('axios'); | |
| const yts = require("yt-search"); | |
| const { getAudio } = require("../lib/ytdl"); | |
| const path = require("path"); | |
| const fs = require("fs"); | |
| const dirTmp = path.join(__dirname, "../tmp"); | |
| const clientId = '8f777f61f80e4051b754d8e50310ad6e'; | |
| const clientSecret = '5802d3726d3149bfb880a577aa855fb3'; | |
| const tokenUrl = 'https://accounts.spotify.com/api/token'; | |
| const base64Credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); | |
| // Pastikan folder tmp ada | |
| if (!fs.existsSync(dirTmp)) { | |
| fs.mkdirSync(dirTmp, { recursive: true }); | |
| } | |
| async function getAccessToken() { | |
| try { | |
| const response = await axios.post(tokenUrl, 'grant_type=client_credentials', { | |
| headers: { | |
| 'Authorization': `Basic ${base64Credentials}`, | |
| 'Content-Type': 'application/x-www-form-urlencoded', | |
| }, | |
| }); | |
| return response.data.access_token; | |
| } catch (error) { | |
| console.error('Gagal mendapatkan token:', error.response?.data || error.message); | |
| return null; | |
| } | |
| } | |
| async function spotifyTrack(trackUrl) { | |
| const { ID3Writer } = await import('browser-id3-writer'); | |
| try { | |
| const match = trackUrl.match(/track\/([a-zA-Z0-9]+)/); | |
| if (!match) throw new Error('URL tidak valid'); | |
| const trackId = match[1]; | |
| // Dapatkan token | |
| const accessToken = await getAccessToken(); | |
| if (!accessToken) throw new Error('Gagal mendapatkan token akses'); | |
| // Ambil metadata Spotify | |
| const trackResponse = await axios.get(`https://api.spotify.com/v1/tracks/${trackId}`, { | |
| headers: { Authorization: `Bearer ${accessToken}` }, | |
| }); | |
| const trackData = trackResponse.data; | |
| // Cari lagu di YouTube | |
| const searchResults = await yts(`${trackData.name} - ${trackData.artists.map(a => a.name).join(', ')}`); | |
| if (!searchResults.videos.length) throw new Error('Video tidak ditemukan di YouTube'); | |
| const videoUrl = searchResults.videos[0].url; | |
| const audio = await getAudio(videoUrl); | |
| if (!audio) throw new Error("Gagal mendapatkan audio dari YouTube"); | |
| // Simpan file dengan nama unik | |
| const Filename = `NSpotify_${Date.now()}_${Math.floor(Math.random() * 1000)}.mp3`; | |
| const FilenameM = `Spotify_${Date.now()}_${Math.floor(Math.random() * 1000)}.mp3`; | |
| await fs.writeFileSync(`${dirTmp}/${Filename}`, audio); | |
| // Tambahkan ID3 Tag | |
| const writer = new ID3Writer(fs.readFileSync(`${dirTmp}/${Filename}`)); | |
| writer | |
| .setFrame('TIT2', trackData.name) | |
| .setFrame('TPE1', trackData.artists.map(a => a.name)) | |
| .setFrame('TALB', trackData.album.name || "") | |
| .setFrame('TYER', parseInt(trackData.album.release_date.split('-')[0])) | |
| .setFrame('APIC', { | |
| type: 3, | |
| data: await urlToBuffer(trackData.album.images[0]?.url), | |
| description: 'Super picture' | |
| }) | |
| .addTag(); | |
| // Simpan file dengan tag ID3 | |
| const taggedSongBuffer = Buffer.from(writer.arrayBuffer); | |
| await fs.writeFileSync(`${dirTmp}/${FilenameM}`, taggedSongBuffer); | |
| const URI = `https://fullpwerr-hutatools.hf.space/tmp/${FilenameM}`; | |
| const res = { | |
| status: 200, | |
| title: trackData.name, | |
| artist: trackData.artists.map(artist => artist.name).join(', '), | |
| album: trackData.album.name, | |
| duration_ms: trackData.duration_ms, | |
| release_date: trackData.album.release_date, | |
| thumbnail: trackData.album.images[0]?.url || null, | |
| music: [{ url: URI }] | |
| }; | |
| console.log(res) | |
| return res | |
| } catch (e) { | |
| return { status: 500, message: e.message} | |
| } | |
| } | |
| async function spotifyPlaylist(playlistUrl) { | |
| try { | |
| const match = playlistUrl.match(/playlist\/([a-zA-Z0-9]+)/); | |
| if (!match) throw new Error('URL playlist tidak valid'); | |
| const playlistId = match[1]; | |
| const accessToken = await getAccessToken(); | |
| if (!accessToken) throw new Error('Gagal mendapatkan token akses'); | |
| const playlistResponse = await axios.get(`https://api.spotify.com/v1/playlists/${playlistId}`, { | |
| headers: { Authorization: `Bearer ${accessToken}` }, | |
| }); | |
| const playlistData = playlistResponse.data; | |
| const tracks = playlistData.tracks.items; | |
| if (!tracks.length) throw new Error('Playlist kosong atau tidak ditemukan'); | |
| console.log(`🎵 Playlist: ${playlistData.name} (${tracks.length} lagu)`); | |
| let results = []; | |
| const limitedTracks = tracks.slice(0, 3); | |
| for (const item of limitedTracks) { | |
| const track = item.track; | |
| if (!track) continue; | |
| const trackUrl = `https://open.spotify.com/track/${track.id}`; | |
| console.log(`🔄 Memproses: ${track.name} - ${track.artists.map(a => a.name).join(', ')}`); | |
| const trackData = await spotifyTrack(trackUrl); | |
| if (trackData) results.push(trackData.music[0].url); | |
| } | |
| return { | |
| status: 200, | |
| playlist_name: playlistData.name, | |
| total_tracks: tracks.length, | |
| cover_image: playlistData.images[0]?.url || null, | |
| tracks: results | |
| }; | |
| } catch (error) { | |
| console.error(error); | |
| return null; | |
| } | |
| } | |
| const urlToBuffer = async (url, options) => { | |
| try { | |
| options ? options : {}; | |
| const res = await axios({ | |
| method: "get", | |
| url, | |
| headers: { | |
| 'DNT': 1, | |
| 'Upgrade-Insecure-Request': 1 | |
| }, | |
| ...options, | |
| responseType: 'arraybuffer' | |
| }); | |
| return res.data; | |
| } catch (e) { | |
| console.log(`Error : ${e}`); | |
| } | |
| }; | |
| module.exports = { | |
| spotifyTrack, | |
| spotifyPlaylist | |
| } |