Загрузка данных
@app.get("/stream/spotify/{spotify_id}")
async def stream_spotify(spotify_id: str):
"""
Высокоскоростной автономный MP3-стриминг ДЛЯ ВСЕХ ПЕСЕН МИРА через spotDL.
Принудительно скармливает под капот spotDL рантайм Deno через правильный флаг --yt-dlp-args!
Отдает чистые байты MP3 в ОЗУ со строгим Content-Length (копия логики SoundCloud).
"""
try:
import os
import io
import tempfile
import shutil
import glob
# Шаг 1. Автоматически зачищаем мусорный префикс sp:, если он прилетел от мобильного плеера
if ":" in spotify_id:
spotify_id = spotify_id.split(":")[-1]
# Собираем прямую, легальную веб-ссылку на трек Спотифая для скачивания
spotify_track_url = f"https://open.spotify.com/track/{spotify_id}"
clean_url = spotify_track_url.replace(" ", "")
# Шаг 2. Создаем изолированную временную папку на сервере
with tempfile.TemporaryDirectory() as tmpdir:
# Настраиваем переменные окружения, чтобы spotDL, yt-dlp и FFmpeg четко видели друг друга
env = os.environ.copy()
env["PATH"] = "/usr/local/bin:/usr/bin:/bin:" + env.get("PATH", "")
# Шаг 3. Запускаем spotDL с жестким указанием аудио-провайдеров и ПРАВИЛЬНЫМ флагом --yt-dlp-args!
# Это мгновенно расшифрует сигнатуры Ютуба для резерва и намертво уничтожит синтаксический краш!
process = await asyncio.create_subprocess_exec(
"spotdl", "download", clean_url,
"--format", "mp3", # Жестко требуем чистейший MP3
"--output", f"{tmpdir}", # Качаем прямо в корень временной папки
"--no-cache", # Отключаем мусорный кэш
"--audio", "soundcloud", "youtube-music", # Сначала SoundCloud, затем Ютуб!
"--yt-dlp-args", "--js-runtimes deno", # ИСПРАВЛЕНО НА ОФИЦИАЛЬНЫЙ ФЛАГ ТВОЕЙ СПРАВКИ!
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
env=env
)
stdout, stderr = await process.communicate()
# Ищем абсолютно любой файл с расширением .mp3 внутри нашей папки
mp3_files = glob.glob(os.path.join(tmpdir, "**", "*.mp3"), recursive=True)
# ЮВЕЛИРНЫЙ ФИКС: берем первый найденный MP3-файл из списка под индексом 0
if not mp3_files or os.path.getsize(mp3_files[0]) == 0:
err_msg = stderr.decode('utf-8', errors='ignore').strip()
out_msg = stdout.decode('utf-8', errors='ignore').strip()
raise HTTPException(
status_code=404,
detail=f"spotDL не смог сгенерировать MP3-файл. Ошибка: {err_msg} | Вывод: {out_msg}"
)
# Шаг 4. Читаем готовые байты НАЙДЕННОГО MP3-файла напрямую в оперативную память сервера
target_file = mp3_files[0]
with open(target_file, "rb") as f:
mp3_bytes = f.read()
# Шаг 5. Отдаем монолитный MP3 файл со строгим размером Content-Length (ОДИН В ОДИН КАК У SOUNDCLOUD!)
from fastapi import Response
return Response(
content=mp3_bytes,
media_type="audio/mpeg",
headers={
"Accept-Ranges": "bytes",
"Content-Length": str(len(mp3_bytes)), # Считаем точный вес файла до последнего бита!
"Cache-Control": "public, max-age=252460800",
"Access-Control-Allow-Origin": "*"
}
)
except HTTPException:
raise
except Exception as e:
import traceback
raise HTTPException(status_code=500, detail=f"Ошибка spotDL движка: {str(e)} | Log: {traceback.format_exc()}")