Last active
November 15, 2025 16:19
-
-
Save ann0nip/8c7c0b58980c5d12a4b50c79a231c09e to your computer and use it in GitHub Desktop.
Automated SoundCloud & YouTube Music Fetcher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # DISCLAIMER: This script is provided for educational and personal use only. | |
| # Downloading copyrighted material without permission may violate laws and terms of service. | |
| # The author assumes no responsibility for any misuse, legal consequences, or damages arising from its use. | |
| # Use at your own risk. | |
| # REQUIREMENTS: | |
| # 1. Python must be installed | |
| # 2. Install with: python3 -m pip install yt-dlp | |
| # (This includes FFmpeg support automatically) | |
| import yt_dlp | |
| import warnings | |
| from datetime import datetime | |
| import os | |
| warnings.filterwarnings("ignore") | |
| # ============================================================ | |
| # TRACK LIST - Add your tracks here | |
| # ============================================================ | |
| track_list = [ | |
| "Rose Royce - Wishing on a star (Bonafique Maccabi Edit)", | |
| "DJ Meme - Saudade", | |
| "Simone Liberali - Rumbero (Original Mix)", | |
| "Daniel Steinberg - Passe (Extended Mix)", | |
| "DJ Soulstar - Sinnerman", | |
| "Angelo Ferreri, Pietro Over Jack - Be Nice (Original Mix)", | |
| "Martin Badder, DJ Rae - Change (Sandy Rivera Remix)", | |
| "Per QX, Reza - When The Lights Go Down (Reza Remix)", | |
| "Cheesecake Boys - Right here Right Now (Original Mix)", | |
| "Crusy - Kiki (Extended Mix)", | |
| "Housequake, Mix Masters - In The Mix (Housequake Extended Reconstruction)", | |
| "Simon Doty - More To Say", | |
| "Mizbee, Maxinne - Want You To Know (Extended Mix)", | |
| "Foals, Hot Since 82 - Into the Surf (Hot Since 82 Remix)", | |
| "Audiojack - Just A Moment", | |
| "Darius Syrossian - Danzer (Original Mix)", | |
| "Timelapse, Adran - Burning (Extended Mix) (Extended Mix)", | |
| "Moontalk - Melody (For Space Lovers) (For Space Lovers)" | |
| ] | |
| # ============================================================ | |
| # Create folder with current date | |
| current_date = datetime.now().strftime("%Y-%m-%d") | |
| folder_name = f"set_{current_date}" | |
| # Create folder if it doesn't exist | |
| if not os.path.exists(folder_name): | |
| os.makedirs(folder_name) | |
| print(f"✓ Carpeta creada: {folder_name}\n") | |
| else: | |
| print(f"✓ Usando carpeta existente: {folder_name}\n") | |
| # Lists to track results | |
| downloaded = [] | |
| failed = [] | |
| # Minimum duration in seconds (e.g., 60 seconds = 1 minute) | |
| MIN_DURATION = 60 | |
| def download_song(query): | |
| """ | |
| Function to download a song by searching first on SoundCloud, then YouTube if not found. | |
| Validates that the track is at least 60 seconds long to avoid geo-restricted previews. | |
| Downloads the FIRST (best) result found and saves it with its original title. | |
| - query: The search string (song title and artist). | |
| """ | |
| ydl_opts = { | |
| 'format': 'bestaudio/best', | |
| 'outtmpl': f'{folder_name}/%(track)s.%(ext)s', | |
| 'extractor_args': { | |
| 'youtube': { | |
| 'player_client': ['android'], | |
| } | |
| }, | |
| 'retries': 10, | |
| 'fragment_retries': 10, | |
| 'ignoreerrors': True, | |
| 'quiet': True, # Silence most output | |
| 'no_warnings': True, # Suppress warnings | |
| 'noprogress': True, # Hide progress bar | |
| 'geo_bypass': True, | |
| 'cachedir': False, | |
| 'user_agent': 'com.google.android.youtube/17.33.35 (Linux; U; Android 12; GB) gzip', | |
| 'noplaylist': True, | |
| 'extract_flat': False, | |
| 'postprocessors': [{ | |
| 'key': 'FFmpegExtractAudio', | |
| 'preferredcodec': 'mp3', | |
| 'preferredquality': '192', | |
| }] | |
| } | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| # Try SoundCloud first | |
| try: | |
| print(f"🔍 Buscando en SoundCloud: {query}") | |
| info = ydl.extract_info('scsearch1:' + query, download=False) | |
| entries = [e for e in info.get('entries', []) if e] | |
| if entries: | |
| best_match = entries[0] | |
| duration = best_match.get('duration', 0) | |
| original_title = best_match.get('track') or best_match.get('title') or query | |
| # Validate duration | |
| if duration and duration < MIN_DURATION: | |
| print(f"⚠️ Duración muy corta ({duration}s) - posible restricción geográfica") | |
| print(f"🔍 Intentando en YouTube...") | |
| else: | |
| duration_min = duration // 60 | |
| duration_sec = duration % 60 | |
| print(f"✓ Encontrado: {original_title} ({duration_min}:{duration_sec:02d})") | |
| print(f"⬇️ Descargando desde SoundCloud...") | |
| ydl.params['outtmpl']['default'] = f'{folder_name}/{original_title}.%(ext)s' | |
| ydl.download([best_match['url']]) | |
| print(f"✅ Descargado exitosamente\n") | |
| downloaded.append({'query': query, 'title': original_title, 'source': 'SoundCloud', 'duration': duration}) | |
| return | |
| else: | |
| print(f"✗ No se encontró en SoundCloud") | |
| except Exception as e: | |
| print(f"✗ Error en SoundCloud") | |
| # Fallback to YouTube | |
| try: | |
| print(f"🔍 Buscando en YouTube: {query}") | |
| info = ydl.extract_info('ytsearch1:' + query, download=False) | |
| entries = [e for e in info.get('entries', []) if e] | |
| if entries: | |
| best_match = entries[0] | |
| duration = best_match.get('duration', 0) | |
| original_title = best_match.get('track') or best_match.get('title') or query | |
| duration_min = duration // 60 | |
| duration_sec = duration % 60 | |
| print(f"✓ Encontrado: {original_title} ({duration_min}:{duration_sec:02d})") | |
| print(f"⬇️ Descargando desde YouTube...") | |
| ydl.params['outtmpl']['default'] = f'{folder_name}/{original_title}.%(ext)s' | |
| ydl.download([best_match['url']]) | |
| print(f"✅ Descargado exitosamente\n") | |
| downloaded.append({'query': query, 'title': original_title, 'source': 'YouTube', 'duration': duration}) | |
| return | |
| else: | |
| print(f"✗ No se encontró en YouTube") | |
| except Exception as e: | |
| print(f"✗ Error en YouTube") | |
| print(f"❌ No se pudo descargar: {query}\n") | |
| failed.append(query) | |
| # Main execution | |
| for track in track_list: | |
| if track.strip(): | |
| print(f"{'='*60}") | |
| print(f"🎵 Procesando: {track}") | |
| print(f"{'='*60}") | |
| download_song(track) | |
| else: | |
| print("Skipping empty query") | |
| # Final summary | |
| print("\n" + "="*60) | |
| print("📊 RESUMEN FINAL") | |
| print("="*60) | |
| print(f"\n✓ Temas descargados exitosamente: {len(downloaded)}/{len(track_list)}") | |
| if downloaded: | |
| for item in downloaded: | |
| duration_min = item['duration'] // 60 | |
| duration_sec = item['duration'] % 60 | |
| print(f" ✓ {item['title']} ({duration_min}:{duration_sec:02d}) - {item['source']}") | |
| if failed: | |
| print(f"\n✗ Temas que no se pudieron descargar: {len(failed)}") | |
| for track in failed: | |
| print(f" ✗ {track}") | |
| else: | |
| print("\n🎉 ¡Todos los temas se descargaron correctamente!") | |
| print(f"\n📁 Ubicación: {folder_name}/") | |
| print("="*60) | |
| print("\n⚠️ DISCLAIMER:") | |
| print("This script is provided for educational and personal use only.") | |
| print("Downloading copyrighted material without permission may violate") | |
| print("laws and terms of service. Use at your own risk.") | |
| print("\n👤 Script by: @ann0nip") | |
| print("="*60) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment