from fastapi import FastAPI, File, UploadFile, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse import os import shutil import librosa import numpy as np from spotipy import Spotify from spotipy.oauth2 import SpotifyClientCredentials app = FastAPI() # CORS for frontend integration app.add_middleware( CORSMiddleware, allow_origins=["*"], # For production, restrict to your frontend URL allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Spotify API client initialization from environment variables (set as HF Secrets) client_id = os.getenv('SPOTIPY_CLIENT_ID') client_secret = os.getenv('SPOTIPY_CLIENT_SECRET') if not client_id or not client_secret: raise RuntimeError("Spotify API credentials not found in environment variables") sp = Spotify(client_credentials_manager=SpotifyClientCredentials(client_id=client_id, client_secret=client_secret)) def extract_fingerprint(audio_path: str): """Extracts a simple MFCC-based fingerprint from the audio file.""" y, sr = librosa.load(audio_path, sr=22050, mono=True) mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13) fingerprint = np.mean(mfcc, axis=1) return fingerprint.tolist() @app.api_route("/recognize", methods=["GET", "POST"]) async def recognize(request: Request, file: UploadFile = File(None)): if request.method == "GET": return JSONResponse( {"message": "Send a POST request with an audio file (field name: 'file') to recognize a song."} ) # POST logic: # Use fallback demo.mp3 if no file uploaded if file is None or file.filename == "": audio_path = "demo.mp3" if not os.path.exists(audio_path): return JSONResponse({"error": "No file uploaded and demo.mp3 not found"}, status_code=400) else: audio_path = f"temp_{file.filename}" with open(audio_path, "wb") as buffer: shutil.copyfileobj(file.file, buffer) try: fingerprint = extract_fingerprint(audio_path) except Exception as e: if audio_path.startswith("temp_") and os.path.exists(audio_path): os.remove(audio_path) return JSONResponse({"error": f"Audio processing failed: {str(e)}"}, status_code=500) # For demo: search Spotify for a fixed song try: results = sp.search(q='Shape of You', type='track', limit=1) if results['tracks']['items']: track = results['tracks']['items'][0] response = { 'title': track['name'], 'artist': track['artists'][0]['name'], 'album': track['album']['name'], 'cover_url': track['album']['images'][0]['url'], 'preview_url': track['preview_url'] } else: response = {'error': 'No match found'} except Exception as e: response = {'error': f'Spotify API error: {str(e)}'} if audio_path.startswith("temp_") and os.path.exists(audio_path): os.remove(audio_path) return JSONResponse(content=response) @app.get("/") def root(): return {"message": "TrueTrace FastAPI backend is running!"}