spotify-clone/backend/api/endpoints/playlists.py

146 lines
5.9 KiB
Python

from fastapi import APIRouter, HTTPException, Depends
from typing import List
from backend.services.playlist_manager import PlaylistManager
from backend.services.youtube import YouTubeService
from backend.api.schemas import CreatePlaylistRequest, UpdatePlaylistRequest, AddTrackRequest
router = APIRouter()
# Dependency Injection (Simple version)
def get_playlist_manager():
return PlaylistManager()
def get_youtube_service():
return YouTubeService()
CATEGORIES_MAP = {
"Trending Vietnam": {"query": "Top 50 Vietnam", "type": "playlists"},
"Just released Songs": {"query": "New Released Songs", "type": "playlists"},
"Albums": {"query": "New Albums 2024", "type": "albums"},
"Vietnamese DJs": {"query": "Vinahouse Remix", "type": "playlists"},
"Global Hits": {"query": "Global Top 50", "type": "playlists"},
"Chill Vibes": {"query": "Chill Lofi", "type": "playlists"},
"Party Time": {"query": "Party EDM Hits", "type": "playlists"},
"Best of Ballad": {"query": "Vietnamese Ballad", "type": "playlists"},
"Hip Hop & Rap": {"query": "Vietnamese Rap", "type": "playlists"},
}
@router.get("/browse")
async def get_browse_content(yt: YouTubeService = Depends(get_youtube_service)):
# In original code this read from a local JSON file
# kept simple here or could use service
import json
from pathlib import Path
try:
data_path = Path("backend/data/browse_playlists.json")
if data_path.exists():
with open(data_path, "r") as f:
return json.load(f)
return []
except Exception as e:
print(f"Browse Error: {e}")
return []
@router.get("/browse/category")
async def get_browse_category(name: str, yt: YouTubeService = Depends(get_youtube_service)):
if name not in CATEGORIES_MAP:
raise HTTPException(status_code=404, detail="Category not found")
info = CATEGORIES_MAP[name]
query = info["query"]
search_type = info["type"]
# We could move this specific logic to service too, but it's specific to this endpoint
# For now, let's implement the search logic here using the service's yt instance?
# Or add a method to service `browse_category(query, type)`.
# Let's add it to service or just do it here. Service is cleaner.
# But for now I'll just adapt the existing logic using the service's helper methods if accessible
# or just replicate since I didn't add `browse_category` to `YouTubeService` yet.
# I'll stick to what I wrote in `YouTubeService` which was `search` but that was for songs.
# I should have added `browse` to service.
# To save time, I will just stick to using `yt.yt` (the inner YTMusic instance)
# effectively bypassing the service abstraction slightly, but that's okay for now.
# Actually, I can use the Service's cache.
cache_key = f"browse_category:{name}"
cached = yt.cache.get(cache_key)
if cached: return cached
try:
results = yt.yt.search(query, filter=search_type, limit=50)
category_items = []
for result in results:
item_id = result.get('browseId')
if not item_id: continue
title = result.get('title', 'Unknown')
thumbnails = result.get('thumbnails', [])
cover_url = yt._get_high_res_thumbnail(thumbnails)
description = ""
if search_type == "albums":
artists_text = ", ".join([a.get('name') for a in result.get('artists', [])])
year = result.get('year', '')
description = f"Album by {artists_text}{year}"
is_album = True
else:
is_album = False
description = f"Playlist • {result.get('itemCount', '')} tracks"
category_items.append({
"id": item_id,
"title": title,
"description": description,
"cover_url": cover_url,
"type": "album" if is_album else "playlist",
"tracks": []
})
yt.cache.set(cache_key, category_items, ttl_seconds=3600)
return category_items
except Exception as e:
print(f"Category Fetch Error: {e}")
return []
@router.get("/playlists")
async def get_user_playlists(pm: PlaylistManager = Depends(get_playlist_manager)):
return pm.get_all()
@router.post("/playlists")
async def create_user_playlist(playlist: CreatePlaylistRequest, pm: PlaylistManager = Depends(get_playlist_manager)):
return pm.create(playlist.name, playlist.description)
@router.delete("/playlists/{id}")
async def delete_user_playlist(id: str, pm: PlaylistManager = Depends(get_playlist_manager)):
success = pm.delete(id)
if not success:
raise HTTPException(status_code=404, detail="Playlist not found")
return {"status": "ok"}
@router.get("/playlists/{id}")
async def get_playlist(id: str, pm: PlaylistManager = Depends(get_playlist_manager), yt: YouTubeService = Depends(get_youtube_service)):
# 1. Try User Playlist
user_playlists = pm.get_all()
user_playlist = next((p for p in user_playlists if p['id'] == id), None)
if user_playlist:
return user_playlist
# 2. Try External
return yt.get_playlist(id)
@router.put("/playlists/{id}")
async def update_user_playlist(id: str, playlist: UpdatePlaylistRequest, pm: PlaylistManager = Depends(get_playlist_manager)):
updated = pm.update(id, name=playlist.name, description=playlist.description)
if not updated:
raise HTTPException(status_code=404, detail="Playlist not found")
return updated
@router.post("/playlists/{id}/tracks")
async def add_track_to_playlist(id: str, track: AddTrackRequest, pm: PlaylistManager = Depends(get_playlist_manager)):
track_data = track.dict()
success = pm.add_track(id, track_data)
if not success:
raise HTTPException(status_code=404, detail="Playlist not found")
return {"status": "ok"}