feat(search): add multi-source search across Yandex, Apple, YouTube with direct download & import

This commit is contained in:
Antigravity committed 2026-10-12 13:53:14 +03:00
1 parent c098376aa4
commit 49bd123a03
5 files changed
+496 -18

No files matched your search

+46 -3
View File
@@ -1,13 +1,17 @@
import os
from fastapi import FastAPI, Request, HTTPException
import urllib.parse
from fastapi import FastAPI, Request, HTTPException, Query
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import FileResponse
from pydantic import BaseModel
from typing import List, Dict, Any
from typing import List, Dict, Any, Optional
from app.config import settings
from app.db import init_db, get_recent_tracks, get_tracks_count
from app.db import init_db, get_recent_tracks, get_tracks_count, get_db
from app.queue import sync_manager
from app.core.search import search_all
from app.core.tagger import get_track_dest_path
app = FastAPI(title="Navidrome Music Sync", version="1.0.0")
@@ -27,6 +31,12 @@ def on_startup():
def read_root(request: Request):
return templates.TemplateResponse(request=request, name="index.html")
@app.get("/api/search")
async def search_music(q: str = Query(..., min_length=1)):
"""Search tracks and albums across Yandex, Apple Music and YouTube."""
results = await search_all(q.strip())
return results
@app.post("/api/download")
async def start_download(req: DownloadRequest):
url = req.url.strip()
@@ -36,6 +46,39 @@ async def start_download(req: DownloadRequest):
task = await sync_manager.start_sync_task(url)
return {"task_id": task.id, "status": task.status}
@app.get("/api/direct-download")
async def direct_download(url: str = Query(...)):
"""Download track directly to user's browser/device."""
provider = sync_manager.get_provider(url)
tracks = await provider.resolve(url)
if not tracks:
raise HTTPException(status_code=404, detail="Track not found")
track = tracks[0]
dest_path = get_track_dest_path(
settings.MUSIC_DIR,
artist=track.artist,
album=track.album or "Singles",
track_num=track.track_number,
title=track.title,
ext="mp3"
)
if not os.path.exists(dest_path):
success = await provider.download_track(track, dest_path)
if not success and track.service == "yandex":
success = await sync_manager.universal_provider.download_track(track, dest_path)
if not success:
raise HTTPException(status_code=500, detail="Failed to download track")
filename = f"{track.artist} - {track.title}.mp3"
encoded_filename = urllib.parse.quote(filename)
return FileResponse(
dest_path,
media_type="audio/mpeg",
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}"}
)
@app.post("/api/sync/yandex-likes")
async def sync_yandex_likes():
if not settings.YANDEX_MUSIC_TOKEN: