AI-RADIO / modal_proxy.py
Nikita Makarov
Improve API key debugging in Modal proxy
72cf384
#!/usr/bin/env python3
"""
YouTube Search Proxy for Modal
Bypasses DNS restrictions in HuggingFace Spaces
"""
import modal
import requests
import json
import os
from typing import Dict, List, Any
# Create Modal app
app = modal.App("youtube-proxy")
# Define image with dependencies
image = modal.Image.debian_slim().pip_install(
"requests",
"fastapi",
"uvicorn"
)
@app.function(image=image, secrets=[modal.Secret.from_name("youtubeapikey")])
@modal.fastapi_endpoint(method="GET", label="youtube-search")
def search_youtube(query: str, limit: int = 5) -> Dict[str, Any]:
"""Search YouTube via official API"""
try:
# YouTube Data API v3 search endpoint
url = "https://www.googleapis.com/youtube/v3/search"
# Get API key from Modal secret
# In Modal, secrets are injected as environment variables
# The secret name "youtubeapikey" should make the key available as YOUTUBE_API_KEY
api_key = os.environ.get("YOUTUBE_API_KEY", "")
if not api_key:
# If not found, try the secret name directly
secret_data = os.environ.get("youtubeapikey", "")
if secret_data:
api_key = secret_data
if not api_key:
# Debug: show what environment variables are available
available_vars = [k for k in os.environ.keys() if 'key' in k.lower() or 'api' in k.lower()]
return {"success": False, "error": f"YouTube API key not found. Check Modal secret 'youtubeapikey' has YOUTUBE_API_KEY variable. Available key vars: {available_vars}", "tracks": []}
params = {
'part': 'snippet',
'q': f"{query} music",
'type': 'video',
'maxResults': limit,
'order': 'relevance',
'safeSearch': 'moderate',
'key': api_key
}
response = requests.get(url, params=params, timeout=30)
response.raise_for_status()
data = response.json()
tracks = []
if 'items' in data:
for item in data['items'][:limit]:
if item.get('id', {}).get('videoId'):
video_id = item['id']['videoId']
snippet = item.get('snippet', {})
track = {
"title": snippet.get('title', 'Unknown'),
"artist": snippet.get('channelTitle', 'Unknown Artist'),
"url": f"https://www.youtube.com/watch?v={video_id}",
"youtube_id": video_id,
"duration": 0,
"genre": query.split()[0] if query else "unknown",
"source": "youtube_api",
"thumbnail": snippet.get('thumbnails', {}).get('default', {}).get('url', '')
}
tracks.append(track)
return {"success": True, "tracks": tracks}
except Exception as e:
return {"success": False, "error": str(e), "tracks": []}
@app.function(image=image)
@modal.fastapi_endpoint(method="GET", label="soundcloud-search")
def search_soundcloud(query: str, limit: int = 5) -> Dict[str, Any]:
"""Search SoundCloud via API"""
try:
# SoundCloud API search
search_query = f"{query} music"
url = f"https://api-v2.soundcloud.com/search/tracks"
params = {
'q': search_query,
'limit': limit,
}
response = requests.get(url, params=params, timeout=30)
if response.status_code == 200:
data = response.json()
tracks = []
if 'collection' in data:
for item in data['collection'][:limit]:
if item.get('streamable'):
track = {
"title": item.get('title', 'Unknown'),
"artist": item.get('user', {}).get('username', 'Unknown Artist'),
"url": item.get('permalink_url', ''),
"duration": item.get('duration', 0) // 1000,
"genre": query.split()[0] if query else "unknown",
"source": "soundcloud_api"
}
tracks.append(track)
return {"success": True, "tracks": tracks}
else:
return {"success": False, "error": f"HTTP {response.status_code}", "tracks": []}
except Exception as e:
return {"success": False, "error": str(e), "tracks": []}
if __name__ == "__main__":
# For local testing
print("Modal YouTube Proxy")
print("Deploy with: modal deploy modal_proxy.py")