File size: 4,683 Bytes
bb97e91
 
 
 
 
 
 
 
cf7cffe
bb97e91
 
 
cf7cffe
bb97e91
 
 
 
 
 
 
 
83317f8
178b50b
bb97e91
 
 
 
 
2c4e0c3
72cf384
 
2c4e0c3
 
72cf384
 
 
 
 
c32d840
72cf384
 
 
2c4e0c3
bb97e91
 
 
 
 
 
 
2c4e0c3
bb97e91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178b50b
bb97e91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/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")