Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ ECIBot is a Discord bot made in Python. It's main purpose is to be a custom bot
* `!search <prompt>`: Search custom sounds that contains given prompt. Alternative command: !b y !buscar.
* `!dalle <prompt>`: Generates 9 images in a 3 by 3 array by sending the given prompt to Dall-e mini API. It may take up to a minute to get the images from the API. Alternative command: !d.
* `!confetti <number>`: Plays the specified number of random Confetti songs. Alternative command: !co.
* `!ytmix <search query or url>`: Plays a mix generated from the given YouTube search or url. Alternative command: !youtubemix
* `!ytmusicmix <search query or url>`: Plays a mix generated from the given YouTube Music search or url. Alternative command: !ytmmix and !youtubemusicmix

## TODO:
* [X] Add more commands.
Expand Down
43 changes: 42 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ async def help(ctx: Context):
embed_msg.add_field(name="!buscar <nombre>", value="Busca sonidos que contengan el argumento añadido. También funciona con !b y !search.", inline=False)
embed_msg.add_field(name="!dalle <texto>", value="Genera imagenes según el texto que se le ha introducido. También funciona con !d.", inline=False)
embed_msg.add_field(name="!confetti <número>", value="Reproduce el número especificado de canciones aleatorias de Confetti. También funciona con !co.", inline=False)
embed_msg.add_field(name="!ytmix <búsqueda o url>", value="Reproduce un mix generado a partir de la búsqueda o url de YouTube introducida. También funciona con !youtubemix.", inline=False)
embed_msg.add_field(name="!ytmusicmix <búsqueda o url>", value="Reproduce un mix generado a partir de la búsqueda o url de YouTube Music introducida. También funciona con !ytmmix y !youtubemusicmix.", inline=False)

await ctx.send(embed=embed_msg)

Expand Down Expand Up @@ -290,13 +292,52 @@ async def youtubemusic(ctx: Context, *args: str):
if "#" not in search_query:
search_query += "#songs"

result_url = yt_music_search_and_get_first_result_url(search_query)
result_url = yt_music_search_and_extract_yt_dlp_info(search_query).get('url')
if result_url is not None:
await play(ctx, result_url)
else:
await ctx.send(":no_entry_sign: No se ha encontrado ningún contenido.")


@bot.command(aliases=["ytmix"], require_var_positional=True)
async def youtubemix(ctx: Context, *, arg: str = ""):
database.register_user_interaction(ctx.author.name, "youtubemix")
if arg.startswith("http://") or arg.startswith("https://"):
if is_youtube_url(arg):
yt_dlp_info = extract_yt_dlp_info(arg)
await play_youtube_mix(ctx, yt_dlp_info)
else:
await ctx.send(f":no_entry_sign: Solo se puede crear un mix de un vídeo de YouTube.")
elif len(arg) > 0:
yt_dlp_info = yt_search_and_extract_yt_dlp_info(arg)
await play_youtube_mix(ctx, yt_dlp_info)


@bot.command(aliases=["ytmmix", "ytmusicmix"], require_var_positional=True)
async def youtubemusicmix(ctx: Context, *, arg: str = ""):
database.register_user_interaction(ctx.author.name, "youtubemusicmix")
if arg.startswith("http://") or arg.startswith("https://"):
if is_youtube_url(arg):
yt_dlp_info = extract_yt_dlp_info(arg)
await play_youtube_mix(ctx, yt_dlp_info)
else:
await ctx.send(f":no_entry_sign: Solo se puede crear un mix de una canción de YouTube.")
elif len(arg) > 0:
yt_music_search_query = arg.rsplit("#", 1)[0] + "#songs"
yt_dlp_info = yt_music_search_and_extract_yt_dlp_info(yt_music_search_query)
await play_youtube_mix(ctx, yt_dlp_info)


async def play_youtube_mix(ctx: Context, yt_dlp_info: Any):
if yt_dlp_info is not None and yt_dlp_info.get('id') is not None:
title = yt_dlp_info.get('title')
video_id = yt_dlp_info.get('id')
await ctx.send(f":twisted_rightwards_arrows: Añadiendo el mix de `{title}`...")
await play(ctx, f"https://www.youtube.com/watch?v={video_id}&list=RD{video_id}")
else:
await ctx.send(":no_entry_sign: No se ha encontrado ningún contenido.")
Comment thread
Wikijito7 marked this conversation as resolved.


def dalle_listener(result: DalleImages):
dalle_results_queue.append(result)
dalle_event.set()
Expand Down
11 changes: 8 additions & 3 deletions youtube.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging as log
import traceback
from typing import Any, Optional
from typing import Any

import yt_dlp
from yt_dlp.extractor.youtube import YoutubeIE

YT_DLP_FORMATS = 'bestaudio/251/250/249/233/234/hls-audio-128000-Audio/m4a/worstaudio/worst'
YT_DLP_EXTRACTORS = yt_dlp.extractor.gen_extractors()
Expand Down Expand Up @@ -39,7 +40,7 @@ def yt_search_and_extract_yt_dlp_info(search_query: str) -> Any:
traceback.print_exc()


def yt_music_search_and_get_first_result_url(search_query: str) -> Optional[str]:
def yt_music_search_and_extract_yt_dlp_info(search_query: str) -> Any:
try:
ydl_opts = {
'format': YT_DLP_FORMATS,
Expand All @@ -48,7 +49,7 @@ def yt_music_search_and_get_first_result_url(search_query: str) -> Optional[str]
'playlist_items': '1', # Only get first item in lists
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
return ydl.extract_info(f"https://music.youtube.com/search?q={search_query}", download=False).get('entries')[0].get('url')
return ydl.extract_info(f"https://music.youtube.com/search?q={search_query}", download=False).get('entries')[0]

except Exception:
log.error("yt_music_search_and_get_first_result_url >> Exception thrown when extracting YouTube Music search info with yt-dlp.")
Expand All @@ -60,3 +61,7 @@ def is_suitable_for_yt_dlp(url: str) -> bool:
if extractor.suitable(url) and extractor.IE_NAME != 'generic':
return True
return False


def is_youtube_url(url: str) -> bool:
return YoutubeIE.suitable(url)