70 lines
2.2 KiB
Python
70 lines
2.2 KiB
Python
import os
|
|
import uuid
|
|
import uvicorn
|
|
from fastapi import FastAPI, HTTPException, BackgroundTasks
|
|
from fastapi.responses import FileResponse
|
|
from pydantic import BaseModel
|
|
from yt_dlp import YoutubeDL
|
|
|
|
app = FastAPI()
|
|
|
|
# Input schema because we aren't savages
|
|
class VideoRequest(BaseModel):
|
|
url: str
|
|
|
|
def cleanup(path: str):
|
|
"""Deletes the file after serving because I know you won't."""
|
|
try:
|
|
os.remove(path)
|
|
print(f"Deleted trash: {path}")
|
|
except Exception as e:
|
|
print(f"Failed to delete {path}: {e}")
|
|
|
|
@app.post("/audio")
|
|
async def download_audio(req: VideoRequest, background_tasks: BackgroundTasks):
|
|
# Random ID so concurrent requests don't overwrite each other
|
|
file_id = str(uuid.uuid4())
|
|
output_template = f"/tmp/{file_id}.%(ext)s"
|
|
|
|
# yt-dlp config: best audio, force mp3 because whisper hates weird codecs
|
|
ydl_opts = {
|
|
'format': 'bestaudio/best',
|
|
'outtmpl': output_template,
|
|
'postprocessors': [{
|
|
'key': 'FFmpegExtractAudio',
|
|
'preferredcodec': 'mp3',
|
|
'preferredquality': '192',
|
|
}],
|
|
'quiet': True,
|
|
'no_warnings': True,
|
|
}
|
|
|
|
try:
|
|
with YoutubeDL(ydl_opts) as ydl:
|
|
print(f"Yoinking audio from: {req.url}")
|
|
info = ydl.extract_info(req.url, download=True)
|
|
# yt-dlp might change extension post-processing, so we find the actual file
|
|
# usually it's .mp3 if we forced it, but let's be safe
|
|
filename = ydl.prepare_filename(info).rsplit('.', 1)[0] + '.mp3'
|
|
|
|
if not os.path.exists(filename):
|
|
raise HTTPException(status_code=500, detail="Download failed, file missing. Blame YouTube.")
|
|
|
|
# queue the deletion so it happens AFTER the response is sent
|
|
background_tasks.add_task(cleanup, filename)
|
|
|
|
return FileResponse(
|
|
path=filename,
|
|
filename=f"{file_id}.mp3",
|
|
media_type='audio/mpeg'
|
|
)
|
|
|
|
except Exception as e:
|
|
print(f"L: {str(e)}")
|
|
raise HTTPException(status_code=400, detail=f"Failed to download: {str(e)}")
|
|
|
|
if __name__ == "__main__":
|
|
# Host on 0.0.0.0 so your docker container isn't useless
|
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
|