Remove Bandcamp scraping (TOS violation), use YouTube links for all recommendations
This commit is contained in:
@@ -2,7 +2,7 @@ from fastapi import FastAPI
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.api.endpoints import auth, bandcamp, billing, lastfm, manual_import, playlist_fix, playlists, profile, recommendations, youtube_music
|
from app.api.endpoints import auth, billing, lastfm, manual_import, playlist_fix, playlists, profile, recommendations, youtube_music
|
||||||
|
|
||||||
app = FastAPI(title="Vynl API", version="1.0.0", redirect_slashes=False)
|
app = FastAPI(title="Vynl API", version="1.0.0", redirect_slashes=False)
|
||||||
|
|
||||||
@@ -22,7 +22,6 @@ app.include_router(recommendations.router, prefix="/api")
|
|||||||
app.include_router(youtube_music.router, prefix="/api")
|
app.include_router(youtube_music.router, prefix="/api")
|
||||||
app.include_router(manual_import.router, prefix="/api")
|
app.include_router(manual_import.router, prefix="/api")
|
||||||
app.include_router(lastfm.router, prefix="/api")
|
app.include_router(lastfm.router, prefix="/api")
|
||||||
app.include_router(bandcamp.router, prefix="/api")
|
|
||||||
app.include_router(profile.router, prefix="/api")
|
app.include_router(profile.router, prefix="/api")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ Already in their library (do NOT recommend these):
|
|||||||
{disliked_instruction}
|
{disliked_instruction}
|
||||||
{exclude_instruction}
|
{exclude_instruction}
|
||||||
|
|
||||||
Respond with exactly {count * 3 if bandcamp_mode else count} music recommendations as a JSON array. Each item should have:
|
Respond with exactly {count} music recommendations as a JSON array. Each item should have:
|
||||||
- "title": song title
|
- "title": song title
|
||||||
- "artist": artist name
|
- "artist": artist name
|
||||||
- "album": album name (if known)
|
- "album": album name (if known)
|
||||||
@@ -216,9 +216,7 @@ Return ONLY the JSON array, no other text."""
|
|||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
return [], remaining
|
return [], remaining
|
||||||
|
|
||||||
from app.services.bandcamp import search_bandcamp_verified
|
# Save to DB with YouTube links
|
||||||
|
|
||||||
# Save to DB — in bandcamp mode, only keep results verified on Bandcamp
|
|
||||||
recommendations = []
|
recommendations = []
|
||||||
for rec in recs_data:
|
for rec in recs_data:
|
||||||
if len(recommendations) >= count:
|
if len(recommendations) >= count:
|
||||||
@@ -226,24 +224,9 @@ Return ONLY the JSON array, no other text."""
|
|||||||
|
|
||||||
artist = rec.get("artist", "Unknown")
|
artist = rec.get("artist", "Unknown")
|
||||||
title = rec.get("title", "Unknown")
|
title = rec.get("title", "Unknown")
|
||||||
bandcamp_url = None
|
|
||||||
youtube_url = None
|
|
||||||
|
|
||||||
# Try Bandcamp for every recommendation
|
# YouTube search link for every recommendation
|
||||||
try:
|
youtube_url = f"https://www.youtube.com/results?search_query={quote_plus(f'{artist} {title} official music video')}"
|
||||||
match = await search_bandcamp_verified(artist, title)
|
|
||||||
if match:
|
|
||||||
bandcamp_url = match.get("bandcamp_url")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if bandcamp_mode and not bandcamp_url:
|
|
||||||
# In bandcamp mode, skip recommendations not found on Bandcamp
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If not on Bandcamp, build a YouTube search link
|
|
||||||
if not bandcamp_url:
|
|
||||||
youtube_url = f"https://www.youtube.com/results?search_query={quote_plus(f'{artist} {title} official music video')}"
|
|
||||||
|
|
||||||
r = Recommendation(
|
r = Recommendation(
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
@@ -254,7 +237,6 @@ Return ONLY the JSON array, no other text."""
|
|||||||
reason=rec.get("reason", ""),
|
reason=rec.get("reason", ""),
|
||||||
score=rec.get("score"),
|
score=rec.get("score"),
|
||||||
query=query,
|
query=query,
|
||||||
bandcamp_url=bandcamp_url,
|
|
||||||
youtube_url=youtube_url,
|
youtube_url=youtube_url,
|
||||||
)
|
)
|
||||||
db.add(r)
|
db.add(r)
|
||||||
|
|||||||
@@ -80,17 +80,7 @@ export default function RecommendationCard({ recommendation, onToggleSave, onDis
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{recommendation.bandcamp_url ? (
|
{recommendation.youtube_url && (
|
||||||
<a
|
|
||||||
href={recommendation.bandcamp_url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="p-2 rounded-full bg-amber-50 text-amber-700 hover:bg-amber-100 transition-colors"
|
|
||||||
title="Listen on Bandcamp"
|
|
||||||
>
|
|
||||||
<ExternalLink className="w-4 h-4" />
|
|
||||||
</a>
|
|
||||||
) : recommendation.youtube_url ? (
|
|
||||||
<a
|
<a
|
||||||
href={recommendation.youtube_url}
|
href={recommendation.youtube_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -100,7 +90,7 @@ export default function RecommendationCard({ recommendation, onToggleSave, onDis
|
|||||||
>
|
>
|
||||||
<ExternalLink className="w-4 h-4" />
|
<ExternalLink className="w-4 h-4" />
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -260,26 +260,6 @@ export default function Discover() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bandcamp Mode Toggle */}
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setBandcampMode(!bandcampMode)}
|
|
||||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors cursor-pointer border-none ${
|
|
||||||
bandcampMode ? 'bg-purple' : 'bg-charcoal-muted/30'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
|
||||||
bandcampMode ? 'translate-x-6' : 'translate-x-1'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<span className="text-sm text-charcoal">
|
|
||||||
Bandcamp mode
|
|
||||||
<span className="text-charcoal-muted ml-1">— prioritize indie & underground artists</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Remaining count */}
|
{/* Remaining count */}
|
||||||
{!user?.is_pro && (
|
{!user?.is_pro && (
|
||||||
|
|||||||
Reference in New Issue
Block a user