diff --git a/backend/.env b/backend/.env index 77a8ece..2a06665 100644 --- a/backend/.env +++ b/backend/.env @@ -1,2 +1,2 @@ -MUSIC_DIR=/music -NAVIDROME_SCAN_URL= +MUSIC_DIR=/root/tunedrop/backend/music +NAVIDROME_SCAN_URL= \ No newline at end of file diff --git a/backend/__pycache__/config.cpython-310.pyc b/backend/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..12898e5 Binary files /dev/null and b/backend/__pycache__/config.cpython-310.pyc differ diff --git a/backend/__pycache__/config.cpython-313.pyc b/backend/__pycache__/config.cpython-313.pyc deleted file mode 100644 index 85283a3..0000000 Binary files a/backend/__pycache__/config.cpython-313.pyc and /dev/null differ diff --git a/backend/__pycache__/downloader.cpython-310.pyc b/backend/__pycache__/downloader.cpython-310.pyc new file mode 100644 index 0000000..391accf Binary files /dev/null and b/backend/__pycache__/downloader.cpython-310.pyc differ diff --git a/backend/__pycache__/downloader.cpython-313.pyc b/backend/__pycache__/downloader.cpython-313.pyc deleted file mode 100644 index 4c4a84d..0000000 Binary files a/backend/__pycache__/downloader.cpython-313.pyc and /dev/null differ diff --git a/backend/__pycache__/main.cpython-310.pyc b/backend/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000..588bed2 Binary files /dev/null and b/backend/__pycache__/main.cpython-310.pyc differ diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc deleted file mode 100644 index 0aff2ff..0000000 Binary files a/backend/__pycache__/main.cpython-313.pyc and /dev/null differ diff --git a/backend/config.py b/backend/config.py index 78f3299..6e38ccd 100644 --- a/backend/config.py +++ b/backend/config.py @@ -1,17 +1,10 @@ -from pydantic_settings import BaseSettings -from pydantic import Field import os +from dotenv import load_dotenv -class Settings(BaseSettings): - PROJECT_ROOT: str = os.path.dirname(os.path.abspath(__file__)) # backend/ - MUSIC_DIR: str = Field( - default=os.path.join(PROJECT_ROOT, "music"), - description="Path where songs are saved" - ) - NAVIDROME_SCAN_URL: str = Field(default="", description="URL to trigger Navidrome rescan") +load_dotenv() - class Config: - env_file = ".env" - env_file_encoding = "utf-8" +class Settings: + MUSIC_DIR = os.getenv("MUSIC_DIR", "/root/tunedrop/backend/music") + NAVIDROME_SCAN_URL = os.getenv("NAVIDROME_SCAN_URL", "") -settings = Settings() +settings = Settings() \ No newline at end of file diff --git a/backend/downloader.py b/backend/downloader.py index 68bff29..31cc503 100644 --- a/backend/downloader.py +++ b/backend/downloader.py @@ -1,75 +1,26 @@ import subprocess -import os -import requests from pathlib import Path from config import settings -def detect_query_type(query: str): - if "youtube.com/playlist" in query or "list=" in query: - return "playlist" - elif "youtube.com/watch" in query or "youtu.be/" in query: - return "video" - else: - return "search" - -def download_song(query: str, single: bool = False): +def download_song(query: str): Path(settings.MUSIC_DIR).mkdir(parents=True, exist_ok=True) - - query_type = detect_query_type(query) - - if query_type == "video": - yt_query = query - playlist_flag = "--no-playlist" - elif query_type == "playlist": - yt_query = query - playlist_flag = None - else: - yt_query = f"ytsearch1:{query.strip()}" if single else f"ytsearch20:{query.strip()}" - playlist_flag = "--no-playlist" - - output_template = str(Path(settings.MUSIC_DIR) / "%(artist)s - %(title)s.%(ext)s") - + output_template = f"{settings.MUSIC_DIR}/%(title)s.%(ext)s" command = [ "yt-dlp", - yt_query, + f"ytsearch1:{query}", "--extract-audio", "--audio-format", "mp3", "--add-metadata", "--output", output_template, - "--print", "%(title)s", - "--quiet" + "--no-playlist", + "--verbose" ] - - if playlist_flag: - command.append(playlist_flag) - - print(f"🎵 Starting download for: {query} (single={single})") - print(f"🛠️ Command: {' '.join(command)}") - + print("Running command:", " ".join(command)) result = subprocess.run(command, capture_output=True, text=True) - + print("STDOUT:", result.stdout) + print("STDERR:", result.stderr) if result.returncode != 0: - print(result.stderr) - raise Exception(f"❌ Download failed:\n{result.stderr}") - - downloaded_titles = [line.strip() for line in result.stdout.strip().split("\n") if line.strip()] - print(f"✅ Finished downloading:\n" + "\n".join(downloaded_titles)) - - for title in downloaded_titles: - filename = f"{title}.mp3" - full_path = os.path.join(settings.MUSIC_DIR, filename) - exists = os.path.exists(full_path) - print(f"📁 Checking file: {filename} → {'✅ Exists' if exists else '❌ Not found'}") - - if settings.NAVIDROME_SCAN_URL: - try: - res = requests.get(settings.NAVIDROME_SCAN_URL, timeout=5) - res.raise_for_status() - print("🔄 Navidrome rescan triggered.") - except Exception as e: - print(f"⚠️ Failed to trigger Navidrome rescan: {e}") - - return { - "query": query, - "downloaded": downloaded_titles - } + raise Exception(f"yt-dlp failed: {result.stderr}") + # Try to find the actual filename + # You can parse result.stdout or list the directory for the latest file + return {"downloaded": "Check music folder for artist - title.mp3"} \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 18fe763..6616c8b 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,25 +1,15 @@ from fastapi import FastAPI, Query, HTTPException from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware -import uvicorn import os from downloader import download_song from config import settings -# Ensure the music directory exists os.makedirs(settings.MUSIC_DIR, exist_ok=True) -print(f"🎯 Music will be saved to: {os.path.join(settings.MUSIC_DIR, '%(artist)s - %(title)s.%(ext)s')}") +print(f"Using MUSIC_DIR: {settings.MUSIC_DIR}") -# List existing files for debug -existing_files = os.listdir(settings.MUSIC_DIR) -print(f"📂 Existing files: {existing_files}") - -app = FastAPI( - title="Tunedrop", - description="🎵 Download music using yt-dlp and stream with Navidrome", - version="1.0.0" -) +app = FastAPI(title="Tunedrop", version="1.0.0") app.add_middleware( CORSMiddleware, @@ -28,19 +18,14 @@ app.add_middleware( allow_headers=["*"], ) -@app.get("/download", summary="Download a song or playlist") -def download( - query: str = Query(..., min_length=2, description="Artist name, song title, or YouTube/playlist link"), - single: bool = Query(False, description="Set to true to download only a single matching song (for search queries)") -): - if not query.strip(): - raise HTTPException(status_code=400, detail="Query cannot be empty") - +@app.get("/download") +def download(query: str = Query(..., min_length=2)): try: - result = download_song(query, single=single) + result = download_song(query) return JSONResponse(content={"status": "success", **result}) except Exception as e: - raise HTTPException(status_code=500, detail=f"Download failed: {str(e)}") - + raise HTTPException(status_code=500, detail=str(e)) + if __name__ == "__main__": - uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) + import uvicorn + uvicorn.run("main:app", host="0.0.0.0", port=8000, log_level="info", reload=True) \ No newline at end of file diff --git a/backend/music/Alan Walker - Alone [FREE]-No Copyright Sounds.mp3 b/backend/music/Alan Walker - Alone [FREE]-No Copyright Sounds.mp3 new file mode 100644 index 0000000..8f11dda Binary files /dev/null and b/backend/music/Alan Walker - Alone [FREE]-No Copyright Sounds.mp3 differ diff --git a/backend/music/Eminem - Mockingbird [Official Music Video].mp3 b/backend/music/Eminem - Mockingbird [Official Music Video].mp3 new file mode 100644 index 0000000..dfafd9d Binary files /dev/null and b/backend/music/Eminem - Mockingbird [Official Music Video].mp3 differ