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"}