from fastapi import FastAPI, Query from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from ytmusicapi import YTMusic import yt_dlp app = FastAPI( title="Farel Music API + Audio Link", description="Backend API Farel Music menggunakan Docker di HF Spaces", version="2.0.0" ) # Aktifkan CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # --- PERBAIKAN DI SINI: Menyisipkan User-Agent agar tidak diblokir SSL/EOF oleh YouTube --- # Kita buat object YTMusic kosong, lalu isi headers-nya secara manual sebelum dipakai. try: yt = YTMusic() # Ganti headers bawaan dengan user-agent browser modern yt.headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.9", "Content-Type": "application/json", "X-Goog-AuthUser": "0", "Origin": "https://music.youtube.com" } except Exception as e: yt = None print(f"Gagal menginisialisasi YTMusic: {e}") def get_direct_audio_url(video_id: str): ydl_opts = { 'format': 'bestaudio/best', 'quiet': True, 'no_warnings': True, 'nocheckcertificate': True, 'preferredcodec': 'm4a', # Tambahkan juga HTTP Headers untuk yt-dlp agar tidak diblokir saat ambil audio 'http_headers': { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': '*/*', 'Accept-Language': 'en-US,en;q=0.9', } } youtube_url = f"https://www.youtube.com/watch?v={video_id}" try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(youtube_url, download=False) return info.get('url') except Exception as e: print(f"Gagal ekstraksi audio: {e}") return None @app.get("/") def home(): return { "status": "Online via Docker", "message": "Farel Music API Ready!", "endpoint": "/search?q=judul_lagu" } @app.get("/search") def search_music(q: str = Query(..., description="Kata kunci pencarian lagu")): if not q.strip(): return JSONResponse(status_code=400, content={"error": "Query tidak boleh kosong."}) if yt is None: return JSONResponse(status_code=500, content={"error": "YTMusic API bermasalah saat inisialisasi."}) try: # Melakukan pencarian results = yt.search(q, filter="songs") formatted_results = [] for item in results[:5]: video_id = item.get('videoId') if not video_id: continue audio_url = get_direct_audio_url(video_id) thumbnails = item.get('thumbnails', []) thumbnail_url = thumbnails[-1]['url'] if thumbnails else None artists = [a['name'] for a in item.get('artists', [])] formatted_results.append({ "id": video_id, "title": item.get('title'), "artists": artists, "album": item.get('album', {}).get('name', 'Single'), "duration": item.get('duration'), "thumbnail": thumbnail_url, "youtube_url": f"https://www.youtube.com/watch?v={video_id}", "audio_url": audio_url }) return { "query": q, "total_results": len(formatted_results), "results": formatted_results } except Exception as e: return JSONResponse(status_code=500, content={"error": f"Terjadi kesalahan: {str(e)}"})