diff --git a/server/fastapi_server/fastapi_server.py b/server/fastapi_server/fastapi_server.py index 977e581..b73892d 100644 --- a/server/fastapi_server/fastapi_server.py +++ b/server/fastapi_server/fastapi_server.py @@ -7,9 +7,14 @@ import uvicorn import aiomysql import os import bcrypt +import asyncio +import subprocess -from pytube import YouTube -from moviepy.editor import AudioFileClip +from yt_dlp import YoutubeDL +from yt_dlp.utils import DownloadError + +#from pytube import YouTube +#from moviepy.audio.io import AudioFileClip #import asyncio @@ -44,45 +49,81 @@ async def fetch_user_info(email: str): await cur.execute("SELECT * FROM users WHERE email = %s", (email,)) return await cur.fetchone() +def download_audio(video_url: str, output_folder: str) -> str: + ydl_opts = { + 'format': 'bestaudio/best', + 'outtmpl': os.path.join(output_folder, '%(title)s.%(ext)s'), + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '192', + }, { + 'key': 'FFmpegMetadata', + 'add_metadata': True, + }], + 'quiet': True, + 'noplaylist': True + } + + with YoutubeDL(ydl_opts) as ydl: + info = ydl.extract_info(video_url, download=True) + filename = ydl.prepare_filename(info) + return os.path.splitext(filename)[0] + ".mp3" # path to final MP3 + +def set_metadata_and_rename(mp3_file: str, name: str, artist: str = None, album: str = None) -> str: + new_file = os.path.abspath(os.path.join(os.path.dirname(mp3_file), f"{name}.mp3")) + + cmd = ["ffmpeg", "-y", "-i", mp3_file] + + # Only add metadata if provided + cmd += ["-metadata", f"title={name}"] + if artist: + cmd += ["-metadata", f"artist={artist}"] + if album: + cmd += ["-metadata", f"album={album}"] + + cmd += [new_file] + + subprocess.run(cmd, check=True) + os.remove(mp3_file) # clean original + return new_file + + # Configuration (customize these!) DOWNLOAD_FOLDER = "downloads" # Where the converted files will be saved if not os.path.exists(DOWNLOAD_FOLDER): os.makedirs(DOWNLOAD_FOLDER) +async def delay_delete_file(delay:float, file:str): + await asyncio.sleep(delay) + if os.path.exists(file): os.unlink(file) @app.post("/convert") async def create_conversion(req: Request): """Converts a YouTube video to MP3.""" - video_url = req.json().get("url") - if (video_url is None): raise HTTPException(400, "Please provide an URL on body") - try: - yt = YouTube(video_url) - stream = yt.streams.filter(only_audio=True).first() - if not stream: - raise HTTPException(status_code=400, detail="No audio stream found.") - - # Download the audio stream - temp_file = os.path.join(DOWNLOAD_FOLDER, f"{yt.title}_temp.mp4") # temporary file - stream.download(output_path=DOWNLOAD_FOLDER, filename=f"{yt.title}_temp") - - # Convert to MP3 using MoviePy - audio_clip = AudioFileClip(os.path.join(DOWNLOAD_FOLDER, f"{yt.title}_temp.mp4")) - mp3_file = os.path.join(DOWNLOAD_FOLDER, f"{yt.title}.mp3") - audio_clip.write_audiofile(mp3_file) - - # Clean up the temporary file - os.remove(os.path.join(DOWNLOAD_FOLDER, f"{yt.title}_temp.mp4")) - - return mp3_file # Return the filename of the converted MP3 - except Exception as e: - print(f"Error converting video: {e}") # Log the error - raise HTTPException(status_code=500, detail=f"Conversion failed: {str(e)}") + body = await req.json() + print(body) + video_url = body.get("video_url") + user_id = body.get("user_id") -class register_model(BaseModel): - first_name:str - last_name:str - email:str - password:str + if (video_url is None): raise HTTPException(400, "Please provide an URL on body") + if (body.get("user_id") is None): raise HTTPException(401, "No User ID") + + USER_FOLDER = os.path.join(DOWNLOAD_FOLDER, str(user_id)) + if not os.path.exists(USER_FOLDER): os.mkdir(USER_FOLDER) + try: + file = await asyncio.to_thread(download_audio, video_url, USER_FOLDER) + except DownloadError as e: + raise HTTPException(500, "Error while downloading " + e.msg) + final_file = await asyncio.to_thread(set_metadata_and_rename, file, body.get("song"), body.get("artist"), body.get("album")) + file_name = final_file.split("/")[-1] + + print("File served: " + final_file) + asyncio.create_task(delay_delete_file(300, final_file)) + + return { "file" : file_name } + + @app.post("/register") async def create_user(req: Request):