import YTMusic from "ytmusic-api" import ystream from 'yt-stream' import fs from 'fs' import process from 'process' import express, { Request, Response } from 'express' const ytmusic = new YTMusic() ytmusic.initialize() const app = express() const PORT = process.env.PORT || 7860 interface SongType { type: "SONG"; name: string; videoId: string; artist: { artistId: string | null; name: string; }; album: { name: string; albumId: string; } | null; duration: number | null; thumbnails: { url: string; width: number; height: number; }[]; } interface VideoType { type: "VIDEO"; name: string; videoId: string; artist: { artistId: string | null; name: string; }; duration: number | null; thumbnails: { url: string; width: number; height: number; }[]; } type MusicType = SongType function bestMatch(query: MusicType[], targetName: string, duration?: number) { // Helper function to calculate a match score const calculateScore = (song: MusicType) => { let score = 0; // Prioritize exact name match if (song.name === targetName) score += 50; else if (song.name.includes(targetName)) score += 20; // Bonus for artist or album relevance if (song.artist.name === "Phong Max") score += 20; if(song.artist.name.includes("Phong Max")) score += 10; if(song.album) { score += 5 if(song.album.name.includes(targetName)) score += 10; } if(duration) { // Duration proximity (ideal around 200-250 seconds) const idealDuration = 211; if(song.duration === null) return 0 score -= Math.abs(song.duration - idealDuration) / 10; // Penalize for duration deviation } return score; }; // Rank songs by score query.sort((a, b) => calculateScore(b) - calculateScore(a)); // Return the best match return query[0]; } // Middleware to parse JSON bodies app.use(express.json()) app.post('/search-stream', async (req: Request, res: Response) => { try { // Extract query from request body const { query } = req.body if (!query) { return res.status(400).json({ error: 'Query is required' }) } // Search for songs let songs = await ytmusic.searchSongs(query) console.log(songs) let musics: MusicType[] = songs.filter((song) => { return song.duration !== null && song.name.toLowerCase().includes(query.toLowerCase()) }) // sort song by their duration musics.sort((a, b) => { if (!a.duration || !b.duration) { return 0 } return b.duration - a.duration }) console.log(musics) const firstSong = bestMatch(musics, query) if (!firstSong) { return res.status(404).json({ error: 'No songs found' }) } if (firstSong.type !== "SONG" && firstSong.type !== "VIDEO") { return res.status(400).json({ error: 'Invalid song type' }) } // console.log(firstSong) const ystreamAgent = new ystream.YTStreamAgent([{ key: 'SOCS', value: 'CAI', domain: 'youtube.com', expires: 'Infinity', sameSite: 'lax', httpOnly: false, hostOnly: false, secure: true, path: '/' }], { localAddress: '0.0.0.0', keepAlive: true, keepAliveMsecs: 5e3 }) ystreamAgent.syncFile(__dirname + "/../cookies.json") ystream.setGlobalAgent(ystreamAgent) ystream.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" // Get stream URL const stream = await ystream.stream(`https://www.youtube.com/watch?v=` + firstSong.videoId, { quality: 'high', type: 'audio', highWaterMark: 1048576 * 32, download: false }) // Return stream URL and song details res.json({ streamUrl: stream.url, songDetails: firstSong }) } catch (error: any) { // if (error.response) { // console.log('Error status:', error.response.status) // // console.log('Error message:', error.response) // } else if (error.request) { // console.log('Error:', error.request) // } else { // console.log('Error:', error.message) // } console.error(error) res.status(500).json({ error: 'Internal server error' }) } }) app.get("/", (req, res) => { res.send("Youtube API Stream are working properly") }) // Start the server app.listen(PORT, () => { console.log(`Server running on port ${PORT}`) })