diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..35d78bc --- /dev/null +++ b/backend/.env @@ -0,0 +1,4 @@ +# MUSIC_DIR=/music +# NAVIDROME_SCAN_URL=http://navidrome:4533/api/rescan +MUSIC_DIR=./backend/music +NAVIDROME_SCAN_URL= diff --git a/backend/__pycache__/config.cpython-313.pyc b/backend/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..53c3e9f Binary files /dev/null and b/backend/__pycache__/config.cpython-313.pyc differ diff --git a/backend/__pycache__/downloader.cpython-313.pyc b/backend/__pycache__/downloader.cpython-313.pyc new file mode 100644 index 0000000..969dfa5 Binary files /dev/null and b/backend/__pycache__/downloader.cpython-313.pyc differ diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..aa7b6c9 Binary files /dev/null and b/backend/__pycache__/main.cpython-313.pyc differ diff --git a/backend/config.py b/backend/config.py index 15700b7..0cf9645 100644 --- a/backend/config.py +++ b/backend/config.py @@ -1,8 +1,10 @@ from pydantic_settings import BaseSettings from pydantic import Field +import os class Settings(BaseSettings): - MUSIC_DIR: str = Field(default="/music", description="Path where songs are saved") + BASE_DIR: str = os.path.dirname(os.path.abspath(__file__)) + MUSIC_DIR: str = Field(default=os.path.join(BASE_DIR, "music"), description="Path where songs are saved") NAVIDROME_SCAN_URL: str = Field(default="", description="URL to trigger Navidrome rescan") class Config: diff --git a/backend/downloader.py b/backend/downloader.py index fc0c89e..f00e972 100644 --- a/backend/downloader.py +++ b/backend/downloader.py @@ -3,26 +3,52 @@ import os from config import settings import requests -def download_song(query: str): - output_template = os.path.join(settings.MUSIC_DIR, "%(artist)s/%(album)s/%(title)s.%(ext)s") +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): + output_template = os.path.join(settings.MUSIC_DIR, "%(uploader)s/%(title)s.%(ext)s") + + query_type = detect_query_type(query) + + if query_type == "video": + yt_query = query + playlist_flag = "--no-playlist" + extra_filters = [] + elif query_type == "playlist": + yt_query = query + playlist_flag = None + extra_filters = [] + else: + yt_query = f"ytsearch20:{query}" + playlist_flag = "--no-playlist" + filter_expr = f"'{query.lower()}' in uploader.lower()" # ✅ valid expression + extra_filters = ["--match-filter", filter_expr] command = [ "yt-dlp", - f"ytsearch1:{query}", + yt_query, "--extract-audio", "--audio-format", "mp3", "--output", output_template, - "--no-playlist", "--quiet" ] + if playlist_flag: + command.append(playlist_flag) + if extra_filters: + command.extend(extra_filters) + result = subprocess.run(command, capture_output=True, text=True) if result.returncode != 0: raise Exception(f"❌ Download failed:\n{result.stderr}") - # Optional: trigger Navidrome rescan if settings.NAVIDROME_SCAN_URL: try: res = requests.get(settings.NAVIDROME_SCAN_URL, timeout=5) diff --git a/backend/main.py b/backend/main.py index f23a499..e29107f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -3,6 +3,10 @@ from fastapi.responses import JSONResponse import uvicorn from downloader import download_song from config import settings +import os + +# Make sure the music directory exists +os.makedirs(settings.MUSIC_DIR, exist_ok=True) app = FastAPI(title="Tunedrop")