Merge pull request 'download-playlist' (#3) from download-playlist into master

Reviewed-on: #3
This commit is contained in:
dvirlabs 2025-08-03 03:08:16 +00:00
commit 154035cc1b
3 changed files with 42 additions and 20 deletions

View File

@ -1,30 +1,52 @@
import subprocess
import yt_dlp
from pathlib import Path
from config import settings
def is_playlist(query: str) -> bool:
return "playlist" in query or "list=" in query
def is_youtube_url(query: str) -> bool:
return query.startswith("http")
def duration_range_filter(min_seconds, max_seconds):
def _filter(info, *, incomplete):
duration = info.get('duration')
if duration:
if duration < min_seconds:
return f"Skipping: {info.get('title')} is shorter than {min_seconds//60} minutes"
if duration > max_seconds:
return f"Skipping: {info.get('title')} is longer than {max_seconds//60} minutes"
return _filter
def download_song(query: str):
Path(settings.MUSIC_DIR).mkdir(parents=True, exist_ok=True)
output_template = f"{settings.MUSIC_DIR}/%(title)s.%(ext)s"
command = [
"yt-dlp",
f"{query if query.startswith('http') else f'ytsearch1:{query}'}",
"--extract-audio",
"--audio-format", "mp3",
"--add-metadata",
"--output", output_template,
"--verbose"
]
# Only add --no-playlist for search queries
if not is_playlist(query) and not query.startswith("http"):
command.append("--no-playlist")
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:
raise Exception(f"yt-dlp failed: {result.stderr}")
if is_youtube_url(query):
yt_query = query
noplaylist = False
elif is_playlist(query):
yt_query = query
noplaylist = False
else:
yt_query = f"ytsearch10:{query}" # Always search for 10 results for any artist/song query
noplaylist = True
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': f"{settings.MUSIC_DIR}/%(title)s.%(ext)s",
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
}, {
'key': 'FFmpegMetadata',
}],
'match_filter': duration_range_filter(2 * 60, 9 * 60), # 2 to 9 minutes
'noplaylist': noplaylist,
'quiet': False,
'verbose': True,
}
print("yt-dlp options:", ydl_opts)
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
result = ydl.download([yt_query])
return {"downloaded": "Check music folder for downloaded files"}