146 lines
5.9 KiB
Python
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"}
|