2023-07-31 23:07:33 -04:00
import asyncio
import re
2023-07-31 21:35:15 -04:00
import discord
2023-08-01 00:03:00 -04:00
import os
2023-07-31 23:07:33 -04:00
from yt_dlp import YoutubeDL
2023-07-31 21:35:15 -04:00
from redbot . core import commands , checks , Config , data_manager
class MusicDownloader ( commands . Cog ) :
def __init__ ( self , bot ) :
self . bot = bot
self . config = Config . get_conf ( self , identifier = 475728338 )
self . config . register_global (
2023-08-01 00:03:00 -04:00
save_directory = str ( data_manager . cog_data_path ( ) ) + f " { os . sep } MusicDownloader "
2023-07-31 21:35:15 -04:00
)
@commands.command ( )
2023-08-01 15:04:07 -04:00
@checks.is_owner ( )
2023-07-31 21:35:15 -04:00
async def change_data_path ( self , ctx : commands . Context , data_path : str = None ) :
""" This command changes the data path this cog outputs to. """
old_path = await self . config . save_directory ( )
2023-08-01 00:03:00 -04:00
if os . path . isdir ( data_path ) :
2023-07-31 21:35:15 -04:00
await self . config . save_directory . set ( data_path )
embed = discord . Embed ( color = await self . bot . get_embed_color ( None ) , description = f " The save directory has been set to ` { data_path } `. \n It was previously set to ` { old_path } `. " )
await ctx . send ( embed = embed )
2023-08-01 00:03:00 -04:00
elif os . path . isfile ( data_path ) :
2023-07-31 21:35:15 -04:00
await ctx . send ( " The path you ' ve provided leads to a file, not a directory! " )
2023-08-01 00:03:00 -04:00
elif os . path . exists ( data_path ) is False :
2023-07-31 21:35:15 -04:00
await ctx . send ( " The path you ' ve provided doesn ' t exist! " )
2023-07-31 23:07:33 -04:00
@commands.command ( aliases = [ " dl " ] )
2023-08-01 13:28:11 -04:00
async def download ( self , ctx : commands . Context , url : str , delete : bool = False , subfolder : str = None ) :
2023-07-31 23:07:33 -04:00
""" This command downloads a YouTube Video as an MP3 to the local music directory. """
def youtube_download ( self , url : str , path : str , message : discord . Message ) :
""" This function does the actual downloading of the YouTube Video. """
class Logger :
def debug ( self , msg ) :
if msg . startswith ( ' [debug] ' ) :
pass
else :
self . info ( msg )
def info ( self , msg ) :
pass
def warning ( self , msg ) :
pass
def error ( self , msg ) :
print ( msg )
message . edit ( msg )
ydl_opts = {
' logger ' : Logger ( ) ,
' format ' : ' m4a/bestaudio/best ' ,
' postprocessors ' : [ { ' key ' : ' FFmpegExtractAudio ' , ' preferredcodec ' : ' m4a ' , } ] ,
2023-08-01 12:27:22 -04:00
' paths ' : { ' home ' : path } ,
' verbose ' : True
2023-07-31 23:07:33 -04:00
}
with YoutubeDL ( ydl_opts ) as ydl :
2023-08-01 13:24:15 -04:00
info = ydl . extract_info ( url = url , download = False )
2023-08-01 00:03:48 -04:00
title = info [ ' title ' ]
2023-08-01 00:15:26 -04:00
id = info [ ' id ' ]
2023-08-01 12:38:45 -04:00
filename = title + f ' [ { id } ].m4a '
2023-08-01 13:22:47 -04:00
full_filename = os . path . join ( data_path , filename )
if os . path . isfile ( full_filename ) :
previously_existed = True
else :
with YoutubeDL ( ydl_opts ) as ydl :
error_code = ydl . download ( url )
previously_existed = False
return filename , previously_existed
2023-07-31 23:07:33 -04:00
data_path = await self . config . save_directory ( )
2023-08-01 13:26:41 -04:00
if subfolder and await self . bot . is_owner ( ctx . author ) :
2023-08-01 00:03:00 -04:00
data_path = os . path . join ( data_path , subfolder )
2023-07-31 23:07:33 -04:00
illegal_chars = r ' <>: " / \ |?* '
if any ( char in illegal_chars for char in subfolder ) :
pattern = " [ " + re . escape ( illegal_chars ) + " ] "
modified_subfolder = re . sub ( pattern , r ' __** \ g<0>**__ ' , subfolder )
await ctx . send ( f " Your subfolder contains illegal characters: ` { modified_subfolder } ` " )
return
2023-08-01 00:03:00 -04:00
elif os . path . isfile ( data_path ) :
2023-07-31 23:07:33 -04:00
await ctx . send ( " Your ' subfolder ' is a file, not a directory! " )
return
2023-08-01 00:03:00 -04:00
elif os . path . exists ( data_path ) is False :
2023-07-31 23:07:33 -04:00
message = await ctx . send ( " Your subfolder does not exist yet, would you like to continue? It will be automatically created. " )
def check ( message ) :
return message . author == ctx . author and message . content . lower ( ) in [ ' yes ' , ' ye ' , ' y ' ]
try :
await self . bot . wait_for ( ' message ' , check = check , timeout = 60 ) # Timeout after 60 seconds
except asyncio . TimeoutError :
await message . edit ( " You took too long to respond. " )
else :
await message . edit ( " Confirmed! " )
try :
2023-08-01 00:03:00 -04:00
os . makedirs ( data_path )
2023-07-31 23:07:33 -04:00
except OSError as e :
await message . edit ( f " Encountered an error attempting to create the subfolder! \n ` { e } ` " )
2023-07-31 23:24:28 -04:00
msg = message . edit
2023-07-31 23:22:59 -04:00
else :
2023-07-31 23:24:28 -04:00
msg = ctx . send
message = await msg ( " YouTube Downloader started! " )
2023-08-01 13:22:47 -04:00
ytdlp_output = youtube_download ( self , url , data_path , message )
2023-08-01 13:25:07 -04:00
full_filename = os . path . join ( data_path , ytdlp_output [ 0 ] )
2023-08-01 12:36:07 -04:00
while not os . path . isfile ( full_filename ) :
2023-08-01 12:40:08 -04:00
await asyncio . sleep ( 0.5 )
2023-08-01 00:15:26 -04:00
if os . path . isfile ( full_filename ) :
with open ( full_filename , ' rb ' ) as file :
2023-08-01 13:25:07 -04:00
complete_message = await ctx . send ( content = " YouTube Downloader completed! \n Downloaded file: " , file = discord . File ( file , ytdlp_output [ 0 ] ) )
2023-08-01 13:09:09 -04:00
file . close ( )
2023-08-01 13:26:41 -04:00
if delete is True or await self . bot . is_owner ( ctx . author ) is False :
2023-08-01 13:25:07 -04:00
if ytdlp_output [ 1 ] is False :
2023-08-01 13:22:47 -04:00
os . remove ( full_filename )
2023-08-01 13:23:45 -04:00
await complete_message . edit ( content = " YouTube Downloader completed! \n File has been deleted from Galaxy. \n Downloaded file: " )