EmTpro01 commited on
Commit
4ede8e7
·
verified ·
1 Parent(s): 967a80f

Upload 17 files

Browse files
.gitattributes CHANGED
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  server/data/processed_songs.csv filter=lfs diff=lfs merge=lfs -text
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  server/data/processed_songs.csv filter=lfs diff=lfs merge=lfs -text
37
+ data/processed_songs.csv filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ EXPOSE 7860
11
+
12
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Create this file in your server/ directory
2
+ import sys
3
+ import os
4
+
5
+ # Add the current directory to the Python path
6
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
7
+
8
+ from app.main import app
9
+
10
+ if __name__ == "__main__":
11
+ import uvicorn
12
+ uvicorn.run(app, host="0.0.0.0", port=7860)
app/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+
app/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (164 Bytes). View file
 
app/__pycache__/main.cpython-312.pyc ADDED
Binary file (9.32 kB). View file
 
app/api/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+
app/api/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (168 Bytes). View file
 
app/api/__pycache__/itunes.cpython-312.pyc ADDED
Binary file (2.06 kB). View file
 
app/api/itunes.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from fastapi import HTTPException
3
+ import logging
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+ async def search_itunes_tracks(query: str, limit: int = 1):
8
+ """
9
+ Search iTunes for tracks with 30-second previews
10
+ """
11
+ base_url = "https://itunes.apple.com/search"
12
+
13
+ try:
14
+ params = {
15
+ "term": query,
16
+ "entity": "song",
17
+ "limit": limit
18
+ }
19
+
20
+ response = requests.get(base_url, params=params)
21
+ response.raise_for_status()
22
+
23
+ results = response.json().get('results', [])
24
+ logger.info(f"Total tracks found: {len(results)}")
25
+
26
+ tracks = []
27
+ for track in results:
28
+ track_info = {
29
+ "name": track.get('trackName'),
30
+ "artist": track.get('artistName'),
31
+ "preview_url": track.get('previewUrl'),
32
+ "full_track_url": track.get('trackViewUrl'),
33
+ "album_image": track.get('artworkUrl100'),
34
+ "genre": track.get('primaryGenreName'),
35
+ "album": track.get('collectionName')
36
+ }
37
+
38
+ if track_info['preview_url']:
39
+ tracks.append(track_info)
40
+
41
+ return tracks[0] if tracks else None
42
+
43
+ except requests.RequestException as e:
44
+ logger.error(f"Error searching iTunes: {e}")
45
+ return None
app/main.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, Query
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse
4
+ import pandas as pd
5
+ import numpy as np
6
+ from sklearn.preprocessing import StandardScaler
7
+ from sklearn.cluster import KMeans
8
+ import joblib
9
+ import requests
10
+ import urllib.parse
11
+ from typing import List, Optional
12
+ import os
13
+
14
+ app = FastAPI(
15
+ title="Vibe ML API",
16
+ description="🎵 AI-powered music recommendation system using K-means clustering",
17
+ version="1.0.0",
18
+ docs_url="/docs",
19
+ redoc_url="/redoc"
20
+ )
21
+
22
+ # Configure CORS for Vercel frontend
23
+ app.add_middleware(
24
+ CORSMiddleware,
25
+ allow_origins=[
26
+ "http://localhost:3000",
27
+ "http://localhost:8000",
28
+ "https://*.vercel.app",
29
+ "https://your-frontend-url.vercel.app", # Replace with your actual Vercel URL
30
+ "*" # For development - remove in production
31
+ ],
32
+ allow_credentials=True,
33
+ allow_methods=["*"],
34
+ allow_headers=["*"],
35
+ )
36
+
37
+ # Global variables for ML models and data
38
+ songs_df = None
39
+ scaler = None
40
+ kmeans_model = None
41
+
42
+ @app.on_event("startup")
43
+ async def startup_event():
44
+ """Load ML models and data on startup"""
45
+ global songs_df, scaler, kmeans_model
46
+
47
+ try:
48
+ # Load your data and models here
49
+ # For now, we'll create a sample dataset
50
+ # Replace this with your actual data loading logic
51
+ print("Loading ML models and data...")
52
+
53
+ # Sample data structure - replace with your actual data
54
+ songs_df = pd.DataFrame({
55
+ 'name': ['Blinding Lights', 'Shape of You', 'Bad Guy', 'Levitating'],
56
+ 'artists': [['The Weeknd'], ['Ed Sheeran'], ['Billie Eilish'], ['Dua Lipa']],
57
+ 'year': [2019, 2017, 2019, 2020],
58
+ 'popularity': [95, 94, 93, 92],
59
+ 'danceability': [0.514, 0.825, 0.711, 0.702],
60
+ 'energy': [0.73, 0.652, 0.430, 0.815],
61
+ 'valence': [0.334, 0.931, 0.436, 0.915]
62
+ })
63
+
64
+ # Initialize scaler and kmeans (replace with your trained models)
65
+ scaler = StandardScaler()
66
+ kmeans_model = KMeans(n_clusters=20, random_state=42)
67
+
68
+ print("✅ Models loaded successfully!")
69
+
70
+ except Exception as e:
71
+ print(f"❌ Error loading models: {e}")
72
+
73
+ @app.get("/")
74
+ async def root():
75
+ """Health check endpoint"""
76
+ return {
77
+ "message": "🎵 Vibe ML API is running!",
78
+ "status": "healthy",
79
+ "version": "1.0.0",
80
+ "endpoints": {
81
+ "docs": "/docs",
82
+ "search": "/search/",
83
+ "recommendations": "/recommendations/",
84
+ "song_details": "/song_details/"
85
+ }
86
+ }
87
+
88
+ @app.get("/search/")
89
+ async def search_songs(
90
+ q: str = Query(..., description="Search query (song name or artist)"),
91
+ limit: int = Query(10, ge=1, le=50, description="Number of results to return")
92
+ ):
93
+ """Search for songs by name or artist"""
94
+ try:
95
+ if not q or len(q.strip()) < 2:
96
+ raise HTTPException(status_code=400, detail="Query must be at least 2 characters long")
97
+
98
+ # iTunes API search
99
+ encoded_query = urllib.parse.quote(q)
100
+ itunes_url = f"https://itunes.apple.com/search?term={encoded_query}&media=music&entity=song&limit={limit}"
101
+
102
+ response = requests.get(itunes_url, timeout=10)
103
+ response.raise_for_status()
104
+
105
+ data = response.json()
106
+ results = data.get('results', [])
107
+
108
+ # Format results
109
+ formatted_results = []
110
+ for item in results:
111
+ formatted_results.append({
112
+ "name": item.get('trackName', 'Unknown'),
113
+ "artists": [item.get('artistName', 'Unknown Artist')],
114
+ "year": int(item.get('releaseDate', '2000')[:4]) if item.get('releaseDate') else 2000,
115
+ "popularity": 50, # Default popularity
116
+ "preview_info": {
117
+ "preview_url": item.get('previewUrl'),
118
+ "album_image": item.get('artworkUrl100', '').replace('100x100', '600x600') if item.get('artworkUrl100') else None
119
+ }
120
+ })
121
+
122
+ return formatted_results
123
+
124
+ except requests.RequestException as e:
125
+ raise HTTPException(status_code=503, detail=f"External API error: {str(e)}")
126
+ except Exception as e:
127
+ raise HTTPException(status_code=500, detail=f"Search error: {str(e)}")
128
+
129
+ @app.get("/song_details/")
130
+ async def get_song_details(
131
+ song_name: str = Query(..., description="Song name"),
132
+ artist_name: str = Query(..., description="Artist name")
133
+ ):
134
+ """Get detailed information about a specific song"""
135
+ try:
136
+ # Search iTunes for the specific song
137
+ query = f"{song_name} {artist_name}"
138
+ encoded_query = urllib.parse.quote(query)
139
+ itunes_url = f"https://itunes.apple.com/search?term={encoded_query}&media=music&entity=song&limit=1"
140
+
141
+ response = requests.get(itunes_url, timeout=10)
142
+ response.raise_for_status()
143
+
144
+ data = response.json()
145
+ results = data.get('results', [])
146
+
147
+ if not results:
148
+ raise HTTPException(status_code=404, detail="Song not found")
149
+
150
+ item = results[0]
151
+
152
+ song_details = {
153
+ "name": item.get('trackName', song_name),
154
+ "artists": [item.get('artistName', artist_name)],
155
+ "year": int(item.get('releaseDate', '2000')[:4]) if item.get('releaseDate') else 2000,
156
+ "popularity": 75, # Default popularity
157
+ "preview_info": {
158
+ "preview_url": item.get('previewUrl'),
159
+ "album_image": item.get('artworkUrl100', '').replace('100x100', '600x600') if item.get('artworkUrl100') else None
160
+ }
161
+ }
162
+
163
+ return song_details
164
+
165
+ except requests.RequestException as e:
166
+ raise HTTPException(status_code=503, detail=f"External API error: {str(e)}")
167
+ except Exception as e:
168
+ raise HTTPException(status_code=500, detail=f"Song details error: {str(e)}")
169
+
170
+ @app.get("/recommendations/")
171
+ async def get_recommendations(
172
+ song_name: str = Query(..., description="Song name for recommendations"),
173
+ artist_name: str = Query(..., description="Artist name"),
174
+ number_songs: int = Query(8, ge=1, le=20, description="Number of recommendations")
175
+ ):
176
+ """Get AI-powered song recommendations"""
177
+ try:
178
+ # For now, return similar songs from iTunes
179
+ # In production, this would use your ML model
180
+
181
+ # Get genre/style from the input song
182
+ query = f"{song_name} {artist_name}"
183
+ encoded_query = urllib.parse.quote(query)
184
+
185
+ # Search for similar songs
186
+ similar_query = urllib.parse.quote(artist_name) # Search by artist for similar style
187
+ itunes_url = f"https://itunes.apple.com/search?term={similar_query}&media=music&entity=song&limit={number_songs + 5}"
188
+
189
+ response = requests.get(itunes_url, timeout=10)
190
+ response.raise_for_status()
191
+
192
+ data = response.json()
193
+ results = data.get('results', [])
194
+
195
+ # Filter out the original song and format results
196
+ recommendations = []
197
+ for item in results:
198
+ if item.get('trackName', '').lower() != song_name.lower():
199
+ recommendations.append({
200
+ "name": item.get('trackName', 'Unknown'),
201
+ "artists": [item.get('artistName', 'Unknown Artist')],
202
+ "year": int(item.get('releaseDate', '2000')[:4]) if item.get('releaseDate') else 2000,
203
+ "popularity": 70, # Default popularity
204
+ "preview_info": {
205
+ "preview_url": item.get('previewUrl'),
206
+ "album_image": item.get('artworkUrl100', '').replace('100x100', '600x600') if item.get('artworkUrl100') else None
207
+ }
208
+ })
209
+
210
+ if len(recommendations) >= number_songs:
211
+ break
212
+
213
+ return recommendations[:number_songs]
214
+
215
+ except requests.RequestException as e:
216
+ raise HTTPException(status_code=503, detail=f"External API error: {str(e)}")
217
+ except Exception as e:
218
+ raise HTTPException(status_code=500, detail=f"Recommendations error: {str(e)}")
219
+
220
+ @app.get("/health")
221
+ async def health_check():
222
+ """Detailed health check"""
223
+ return {
224
+ "status": "healthy",
225
+ "timestamp": "2025-06-19T06:13:28Z",
226
+ "models_loaded": songs_df is not None,
227
+ "api_version": "1.0.0"
228
+ }
229
+
230
+ if __name__ == "__main__":
231
+ import uvicorn
232
+ uvicorn.run(app, host="0.0.0.0", port=7860)
app/models/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+
app/models/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (171 Bytes). View file
 
app/models/__pycache__/schemas.cpython-312.pyc ADDED
Binary file (1.58 kB). View file
 
app/models/schemas.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+ from typing import List, Optional
3
+
4
+ class Song(BaseModel):
5
+ name: str
6
+ artists: List[str]
7
+ year: int
8
+ popularity: int
9
+
10
+ class Recommendation(BaseModel):
11
+ name: str
12
+ artists: List[str]
13
+ year: int
14
+ popularity: int
15
+ danceability: float
16
+ energy: float
17
+ valence: float
18
+
19
+ class TrackInfo(BaseModel):
20
+ name: str
21
+ artist: str
22
+ preview_url: Optional[str]
23
+ full_track_url: Optional[str]
24
+ album_image: Optional[str]
25
+ genre: Optional[str]
26
+ album: Optional[str]
27
+
28
+ class RecommendationWithPreview(Recommendation):
29
+ preview_info: Optional[TrackInfo] = None
data/processed_songs.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:358df3704e9d2457b6f1b8383137d877903907a3cf73b5773c95797c93355427
3
+ size 24572270
data/song_cluster_pipeline.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ceee8795f25c27031dbc76f2a9365dc2d6bbde4dfc51788f4aa089a09f059946
3
+ size 686654
requirements.txt ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ annotated-types==0.7.0
2
+ anyio==4.8.0
3
+ certifi==2024.12.14
4
+ charset-normalizer==3.4.1
5
+ click==8.1.8
6
+ colorama==0.4.6
7
+ exceptiongroup==1.2.2
8
+ fastapi==0.115.6
9
+ h11==0.14.0
10
+ idna==3.10
11
+ joblib==1.4.2
12
+ numpy==2.2.2
13
+ pandas==2.2.3
14
+ pydantic==2.10.5
15
+ pydantic_core==2.27.2
16
+ python-dateutil==2.9.0.post0
17
+ python-multipart==0.0.20
18
+ pytz==2024.2
19
+ requests==2.32.3
20
+ scikit-learn==1.6.1
21
+ scipy==1.15.1
22
+ six==1.17.0
23
+ sniffio==1.3.1
24
+ starlette==0.41.3
25
+ threadpoolctl==3.5.0
26
+ typing_extensions==4.12.2
27
+ tzdata==2024.2
28
+ urllib3==2.3.0
29
+ uvicorn==0.34.0