From 1a0948d52b0c5c705a161a76216c895d56224e4e Mon Sep 17 00:00:00 2001 From: dvirlabs Date: Sun, 3 Aug 2025 02:26:47 +0000 Subject: [PATCH 1/2] Update duration and max songs --- backend/__pycache__/config.cpython-310.pyc | Bin 590 -> 590 bytes .../__pycache__/downloader.cpython-310.pyc | Bin 1183 -> 1777 bytes backend/downloader.py | 62 ++++++++++++------ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/backend/__pycache__/config.cpython-310.pyc b/backend/__pycache__/config.cpython-310.pyc index 9327efc71046fb460c425ce25594da5c8cc17cd2..cd0b255ef5b77da6110b779ee0c5308937aaf146 100644 GIT binary patch delta 19 ZcmX@da*l;7pO=@50SJEW-pJ*`1OPJK1o!{| delta 19 ZcmX@da*l;7pO=@50SI31+Q{X?1OPH}1m*w$ diff --git a/backend/__pycache__/downloader.cpython-310.pyc b/backend/__pycache__/downloader.cpython-310.pyc index 71f9947853490b96cf19ea198e8cb32f7d1aba76..d0dbdb15da0682d7cc0830d49cb969c08c80430a 100644 GIT binary patch literal 1777 zcmZuxUvC>l5Wn5~>+_$uPN4#dV2cl3Fr9`SkBSfg6dB8f~uI;POx5w_f zi7opnk?0HX0oq8(V?F_2fzPmy@RWC+kZ72>#7X#br=6Xho!Ob4o!^LRHMHRS<=3AE zpLmw_J1(w%4lEwPP~QPz79**(jWbQjh;CDYyq(%3XWIeZ;VyHz*K?T1{C#KJzaj}( z2_yjvS$W^uF5ym(vI?t$G-PDUYSm7_hqc)v#`!YR1DFHFwNA3WGVb?dE+@v5T#scI z6VyEakf$)#VW>I~MrPJ?EB6`284k8LO^~P2Bu$i_x=24gwvLI!%SnGh{=C(eLg=<0 zXPilqx4Th1kwi)!mH9v z;|#RU;f|rwk<@A@(E}NP)@4Al1WQu`e-km*<1X)vWr`|=%aF(_@c(d$p`c3~MJj%< zdV~>1_k%f^p<{dV!T|Gzig<}-G*|_)h;dG5RK7K%FRY;>-@!~m?Di3P36K23V(!}( zaNdEt1qE<6T0Uf%jb)@0kxj#`VUp+YbgNC{>O`k}T5l$*nTo8>Wm6BLta-8q4yQqL zlw@Pg739M1bKSCylVm+%!X%5uC{MZOvId&R#FR(TZb$K0WK5mqsYsFE=_M&>Rqz&= zppaC^O%j|=Z=t~=sB^8AwN%7Ws_w6x3yPCkRK5Y0@=YW~Oir|oW6qGu_Zeusv<@g(5U}tW>|d^#k6z$uf&XtH%jg3*x`=9S>8O8cDR5tb~{}3G7Lq0LJT=E{5lCxMa z9?N&Zqp6)4J?1)Mk&cpY0Us^jcs(IUkv2Y{N<)Exu~0gfBIZg7sZ0fSz~fZ_2u!khsnUeH zg~G2&1g=Cdn$qJZTaVT|4j|2p1orFL`kWf1$lv3inb{u+2|MEOa@b~@*mnx)2!qb4(Uk6lv1c$WB@-9qR zFM5zB-699Z18nz_{#h<;rKNGYLZrq0&oY5fD-O64Y=PGTs=?YwuyM-oW63FO8v!7+ MLK?JT2X@2%8+Et5S^xk5 literal 1183 zcmY*Z!EW0|5ali@k&-P-RnpW)6=>S%zz9K$9E7$AVCRrS-NJS*1`2`VtSm|-m)#vE z79n5iT+<&20@%k~`Vsw%z4p{kXfEw6Wi{yvJM%a@J2N}`RTdCD?=#+k)#>So@VN7xe$Xo%*_gC_W|yffcgW*h=&(N_TM z#2L2#qDaoskQwi%21Dd)=0@&Jl;S@e_ zt0}O1WP&1%R%xQKYFPQ*J7>t0J%aML;{6vRB_)oq%mpY}jHXe%6giAQzRXh@flv>N zmDx%3WE-quQ`+1;rdp)_SC@@$jdfjr5I_gnf?u(P3ygmJH8sgv-cY07@=FHZ+PU_i zal_R9+R3R^Yv+yg)~@@uQP75+HG|#r)+NWr+PPx-o9zxl{JII98%8mG*n{qMu;w@H z%I0V-+G}3t?7em`U0VNS?JjoeH6QwfcP%E}fjdiA{fv8X*II4%EA}gcPvKr-ujsR* z(;+v05h;W}U}zywn<)DQ1J5EnSqRda7ehd(|)9oOLL@$pz$E>+!4 z-Gf<_qyokR)BcYEP0wt{Vknn{Y5nm0X!-5Yv!0{R1`!u( z6z29lND!v=^dc4oCNdv-HMLE5bJ_`tj2eW(__tlL??{&^ejvXJY@MC$DJELx=hmdZ zBypGRGS2rIXWn-H-EX&9kN0S0ml0wCQ=gOTu%-5GB%%n;(_~sV!8Bu;&yw>^GhB_z mcvC6U`sDStj|!>ZV_i+~Z6={i#rO7BwEZ^<9GkAs`|iK7qCCw2 diff --git a/backend/downloader.py b/backend/downloader.py index d4abd16..2c4884e 100644 --- a/backend/downloader.py +++ b/backend/downloader.py @@ -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 max_duration_filter(max_seconds): + def _filter(info, *, incomplete): + duration = info.get('duration') + if duration and 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 + elif len(query.split()) == 1: + yt_query = f"ytsearch50:{query}" + noplaylist = True + else: + yt_query = f"ytsearch1:{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': max_duration_filter(10 * 60), # 5 minutes in seconds + '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"} \ No newline at end of file From fdd95d73f3682500c70f7cdc593f0affd496f402 Mon Sep 17 00:00:00 2001 From: dvirlabs Date: Sun, 3 Aug 2025 02:54:48 +0000 Subject: [PATCH 2/2] Get more accurate when search artist name --- .../__pycache__/downloader.cpython-310.pyc | Bin 1777 -> 1804 bytes backend/downloader.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/__pycache__/downloader.cpython-310.pyc b/backend/__pycache__/downloader.cpython-310.pyc index d0dbdb15da0682d7cc0830d49cb969c08c80430a..665b4d12df9f6f8386091ab926391611f6528cb5 100644 GIT binary patch delta 695 zcmY*WJ8u&~5Z;;Hd-%>ac9eiYh8QFwEE0*xNIXPKK>>}6C=HgCaqP?EEbU!Hu+B9*n)yYa()6063|lf1JYBZ07OB@YtD)#va9)SzuE6*wBP*5?dNmdT+S1`zI=Y! z`S3RP4wf*br%Up88qo}sp%}tC5)i&{`FT(pD{-e8R!$yp z@19%P4#J(d74-_KVyhdrrvkH&S6A@Z-)`Zo1wLU{H%}G zJ-BXuT6Y&yf1>*gv*4lvn@vs8<1#E1U_-Pkb?dOXSNoIPFar$=tCD%lqXW$VGS-%3jD(x zeb{A!?=WFbGs>)ev&sbf8x?yobHs+Msi+lq+-f6gx1)Gsb<(I1yK?-Tk&cr z168lZ!NGO9u;T>lQM{qTM1RDy7G&OfZBzfmVA`Rs3k4{HGw93^yL7x=-!M+!L^(kN zaxw=yhw&VS2(q#NhnH5OrV6D;jf4{vfO9#ed&VjFsy`U#w*qCRY#U@bWnBi?^StzZpF4`(06kZ^^!hoT_-2v$)!qPPty3WLNR z-m|-g8WN?{*hXN=?Rlx;_Tj}ym#4f}(0BO6{Sh%5L6yz2r2Yti<^GUD9PyR0#d{UZ z>~>9tu}U&0+>8>n)N0&5$p5?JJyuCrlgsrhmlrzjaWSi3^EHef=(70|eErruSImrN z7-weEZbpjq_+rk(jGnZv-5t%9x bool: def is_youtube_url(query: str) -> bool: return query.startswith("http") -def max_duration_filter(max_seconds): +def duration_range_filter(min_seconds, max_seconds): def _filter(info, *, incomplete): duration = info.get('duration') - if duration and duration > max_seconds: - return f"Skipping: {info.get('title')} is longer than {max_seconds//60} minutes" + 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): @@ -24,11 +27,8 @@ def download_song(query: str): elif is_playlist(query): yt_query = query noplaylist = False - elif len(query.split()) == 1: - yt_query = f"ytsearch50:{query}" - noplaylist = True else: - yt_query = f"ytsearch1:{query}" + yt_query = f"ytsearch10:{query}" # Always search for 10 results for any artist/song query noplaylist = True ydl_opts = { @@ -40,7 +40,7 @@ def download_song(query: str): }, { 'key': 'FFmpegMetadata', }], - 'match_filter': max_duration_filter(10 * 60), # 5 minutes in seconds + 'match_filter': duration_range_filter(2 * 60, 9 * 60), # 2 to 9 minutes 'noplaylist': noplaylist, 'quiet': False, 'verbose': True,