mirror of
https://github.com/wahyd4/you-music.git
synced 2026-08-08 20:59:47 +10:00
225 lines
7.5 KiB
Python
225 lines
7.5 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
|
|
from app.db.session import get_db
|
|
from app.models.models import Music
|
|
from app.schemas.schemas import DownloadRequest
|
|
from app.services.downloader import music_downloader
|
|
from app.services.download_queue import download_queue
|
|
from app.core.config import settings
|
|
import os
|
|
import uuid
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
async def process_download(
|
|
task_id: str,
|
|
url: str,
|
|
title: str,
|
|
db: AsyncSession,
|
|
add_to_playlist: str = None
|
|
):
|
|
"""Background task to download music"""
|
|
await download_queue.update_status(task_id, "downloading", progress=0.0)
|
|
|
|
success, file_path, error = await music_downloader.download_music(url, title)
|
|
|
|
if success and file_path:
|
|
await download_queue.update_status(task_id, "downloading", progress=80.0)
|
|
# Extract metadata
|
|
metadata = await music_downloader.get_music_metadata(file_path)
|
|
|
|
# Determine source type
|
|
source_type = "youtube" if music_downloader.is_youtube_url(url) else \
|
|
"bilibili" if music_downloader.is_bilibili_url(url) else "other"
|
|
|
|
# Get relative path and file format
|
|
relative_path = os.path.relpath(file_path, settings.MUSIC_DIR)
|
|
file_extension = os.path.splitext(file_path)[1][1:] # Get extension without dot
|
|
|
|
# Create database record
|
|
db_music = Music(
|
|
title=metadata.get("title", title or "Unknown"),
|
|
artist=metadata.get("artist", "Unknown"),
|
|
album=metadata.get("album", ""),
|
|
duration=metadata.get("duration", 0),
|
|
file_path=relative_path,
|
|
file_size=os.path.getsize(file_path),
|
|
file_format=file_extension,
|
|
file_location=file_path,
|
|
file_exists=True,
|
|
source_url=url,
|
|
source_type=source_type,
|
|
thumbnail=metadata.get("thumbnail") # Add thumbnail
|
|
)
|
|
|
|
db.add(db_music)
|
|
await db.commit()
|
|
await db.refresh(db_music)
|
|
|
|
await download_queue.update_status(task_id, "completed", progress=100.0, music_id=db_music.id)
|
|
|
|
# Add to playlist if specified
|
|
if add_to_playlist:
|
|
from app.models.models import Playlist
|
|
result = await db.execute(
|
|
select(Playlist).where(Playlist.name == add_to_playlist)
|
|
)
|
|
playlist = result.scalar_one_or_none()
|
|
if playlist:
|
|
playlist.music_items.append(db_music)
|
|
await db.commit()
|
|
else:
|
|
await download_queue.update_status(task_id, "failed", error=error or "Download failed")
|
|
|
|
|
|
@router.post("/music")
|
|
async def download_music(
|
|
request: DownloadRequest,
|
|
background_tasks: BackgroundTasks,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""Download music from URL"""
|
|
# Validate URL
|
|
if not request.url:
|
|
raise HTTPException(status_code=400, detail="URL is required")
|
|
|
|
# Check if already downloaded
|
|
result = await db.execute(
|
|
select(Music).where(Music.source_url == request.url)
|
|
)
|
|
existing = result.scalar_one_or_none()
|
|
if existing:
|
|
return {
|
|
"message": "Music already downloaded",
|
|
"music_id": existing.id,
|
|
"status": "existing",
|
|
"task_id": None
|
|
}
|
|
|
|
# Create task ID and add to queue
|
|
task_id = str(uuid.uuid4())
|
|
await download_queue.add_task(
|
|
task_id,
|
|
request.url,
|
|
request.title or "Unknown",
|
|
thumbnail=request.thumbnail,
|
|
artist=request.artist
|
|
)
|
|
|
|
# Start download in background
|
|
background_tasks.add_task(
|
|
process_download,
|
|
task_id,
|
|
request.url,
|
|
request.title,
|
|
db,
|
|
request.add_to_playlist
|
|
)
|
|
|
|
return {
|
|
"message": "Download started",
|
|
"status": "downloading",
|
|
"task_id": task_id
|
|
}
|
|
|
|
|
|
@router.post("/playlist")
|
|
async def download_playlist(
|
|
request: DownloadRequest,
|
|
background_tasks: BackgroundTasks,
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""Download entire playlist"""
|
|
if not request.url:
|
|
raise HTTPException(status_code=400, detail="URL is required")
|
|
|
|
playlist_name = request.title or "Downloaded Playlist"
|
|
|
|
async def process_playlist_download():
|
|
success, files, error = await music_downloader.download_playlist(
|
|
request.url,
|
|
playlist_name
|
|
)
|
|
|
|
if success:
|
|
# Create playlist in database
|
|
from app.models.models import Playlist
|
|
result = await db.execute(
|
|
select(Playlist).where(Playlist.name == playlist_name)
|
|
)
|
|
playlist = result.scalar_one_or_none()
|
|
|
|
if not playlist:
|
|
playlist = Playlist(name=playlist_name)
|
|
db.add(playlist)
|
|
await db.commit()
|
|
await db.refresh(playlist)
|
|
|
|
# Add all downloaded files to database and playlist
|
|
for file_path in files:
|
|
metadata = await music_downloader.get_music_metadata(file_path)
|
|
relative_path = os.path.relpath(file_path, settings.MUSIC_DIR)
|
|
|
|
# Check if already in database
|
|
result = await db.execute(
|
|
select(Music).where(Music.file_path == relative_path)
|
|
)
|
|
existing_music = result.scalar_one_or_none()
|
|
|
|
if not existing_music:
|
|
db_music = Music(
|
|
title=metadata.get("title", os.path.basename(file_path)),
|
|
artist=metadata.get("artist", "Unknown"),
|
|
album=metadata.get("album", ""),
|
|
duration=metadata.get("duration", 0),
|
|
file_path=relative_path,
|
|
file_size=os.path.getsize(file_path),
|
|
source_url=request.url,
|
|
source_type="youtube" if music_downloader.is_youtube_url(request.url) else "bilibili"
|
|
)
|
|
db.add(db_music)
|
|
await db.commit()
|
|
await db.refresh(db_music)
|
|
|
|
playlist.music_items.append(db_music)
|
|
|
|
await db.commit()
|
|
|
|
background_tasks.add_task(process_playlist_download)
|
|
|
|
return {
|
|
"message": "Playlist download started",
|
|
"status": "downloading"
|
|
}
|
|
|
|
|
|
@router.get("/status")
|
|
async def get_download_status():
|
|
"""Get current download status"""
|
|
all_tasks = await download_queue.get_all_tasks()
|
|
|
|
return {
|
|
"tasks": [task.model_dump() for task in all_tasks],
|
|
"active_downloads": len([t for t in all_tasks if t.status == "downloading"]),
|
|
"pending": len([t for t in all_tasks if t.status == "pending"]),
|
|
"completed": len([t for t in all_tasks if t.status == "completed"]),
|
|
"failed": len([t for t in all_tasks if t.status == "failed"])
|
|
}
|
|
|
|
|
|
@router.delete("/task/{task_id}")
|
|
async def remove_download_task(task_id: str):
|
|
"""Remove a download task from the queue"""
|
|
await download_queue.remove_task(task_id)
|
|
return {"message": "Task removed"}
|
|
|
|
|
|
@router.post("/clear-completed")
|
|
async def clear_completed_tasks():
|
|
"""Clear all completed and failed tasks"""
|
|
await download_queue.clear_completed()
|
|
return {"message": "Completed tasks cleared"}
|