Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix music in general and update #80

Merged
merged 1 commit into from
May 29, 2022
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
6 changes: 3 additions & 3 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# email theophile.diot900@gmail.com
# linting: black
# -----------------------------------------------------------
from asyncio import get_event_loop
from asyncio import new_event_loop
from datetime import date
from itertools import chain
from logging import basicConfig, DEBUG, error, info, INFO
Expand Down Expand Up @@ -407,9 +407,9 @@ async def setup(self, **kwargs):
level=DEBUG if getenv("ENV") == "DEVELOPMENT" else INFO,
) # Configure the logging

system("cls" if name == "nt" else "clear")
# system("cls" if name == "nt" else "clear")
print("Omnitron is starting...")
loop = get_event_loop()
loop = new_event_loop()
try:
loop.run_until_complete(Omnitron.setup()) # Starts the bot
except KeyboardInterrupt:
Expand Down
181 changes: 136 additions & 45 deletions cogs/dj/play.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from cmath import atan
from functools import wraps
from inspect import Parameter
from os import getenv
from re import compile as re_compile
from typing import Union
from typing import List, Union
from youtube_dl import utils, YoutubeDL

from disnake import (
ApplicationCommandInteraction,
Attachment,
Client as botClient,
Colour,
Embed,
NotFound,
VoiceClient,
Expand Down Expand Up @@ -103,6 +106,25 @@ def __init__(self, bot: Omnitron):
self.yt_rx = re_compile(
r"http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?‌​[\w\?‌​=]*)?"
)
utils.bug_reports_message = lambda: ""
self.ytdl = YoutubeDL(
{
"format": "worstaudio/worst",
"outtmpl": "temp/musics/%(title)s.%(ext)s",
"download_archive": "temp/musics.txt",
"restrictfilenames": True,
"noplaylist": True,
"nocheckcertificate": True,
"ignoreerrors": False,
"logtostderr": False,
"quiet": True,
"no_warnings": True,
"verbose": False,
"default_search": "auto",
# bind to ipv4 since ipv6 addresses cause issues sometimes
"source_address": "0.0.0.0",
}
)

""" EVENTS """

Expand Down Expand Up @@ -159,24 +181,9 @@ async def check(
self,
source,
query: str = None,
audio_file: Attachment = None,
):
"""This check ensures that the bot and command author are in the same voicechannel."""
try:
player = self.bot.lavalink.player_manager.create(
source.guild.id, endpoint=str(source.guild.region)
)
except NodeException:
if isinstance(source, Context):
return await source.reply(
f"⚠️ - {source.author.mention} - The player is not ready yet, please try again in a few seconds!",
delete_after=20,
)
else:
return await source.response.send_message(
f"⚠️ - {source.author.mention} - The player is not ready yet, please try again in a few seconds!",
ephemeral=True,
)

# Create returns a player if one exists, otherwise creates.
# This line is important because it ensures that a player always exists for a guild.

Expand All @@ -202,6 +209,22 @@ async def check(
ephemeral=True,
)

try:
player = self.bot.lavalink.player_manager.create(
source.guild.id, endpoint=source.author.voice.channel.rtc_region
)
except NodeException:
if isinstance(source, Context):
return await source.reply(
f"⚠️ - {source.author.mention} - The player is not ready yet, please try again in a few seconds!",
delete_after=20,
)
else:
return await source.response.send_message(
f"⚠️ - {source.author.mention} - The player is not ready yet, please try again in a few seconds!",
ephemeral=True,
)

if not player.is_connected:
if source.author.voice.channel == source.guild.afk_channel:
if isinstance(source, Context):
Expand Down Expand Up @@ -249,7 +272,7 @@ async def check(
ephemeral=True,
)

return await function(self, source, query)
return await function(self, source, query, audio_file=audio_file)

return check

Expand All @@ -259,30 +282,30 @@ async def check(
name="play",
aliases=["sc_p"],
usage="<link>|<Title>",
description="Plays a link or title from a SoundCloud/YouTube song! It can also play attachments! (supports playlists!)",
description="Plays a link or title from a YouTube/SoundCloud song or an audio file! (supports playlists!)",
)
@Utils.check_bot_starting()
@Utils.check_dj()
@bot_has_guild_permissions(connect=True, speak=True)
@bot_has_permissions(send_messages=True, embed_links=True)
@__ensure_voice
@max_concurrency(1, per=BucketType.guild)
async def play_command(self, source: Context, query: str = None):
async def play_command(self, source: Context, query: str = None, **kwargs):
"""
This command plays a link or title from a SoundCloud/YouTube song! (supports playlists!)
This command plays a link or title from a YouTube/SoundCloud song or an audio file! (supports playlists!)
Parameters
----------
source: :class:`disnake.ext.commands.Context`
The command context
query: :class:`str` optional
The SoundCloud/YouTube link or title
The link or YouTube/SoundCloud title
"""
await self.handle_play(source, query)

@slash_command(
name="play",
description="Plays a link or title from a SoundCloud/YouTube song! (supports playlists!)",
description="Plays a link or title from a YouTube/SoundCloud song or an audio file! (supports playlists!)",
)
@guild_only()
@Utils.check_bot_starting()
Expand All @@ -295,25 +318,30 @@ async def play_slash_command(
self,
source: ApplicationCommandInteraction,
query: str = None,
audio_file: Attachment = None,
):
"""
This slash command plays a link or title from a SoundCloud/YouTube song! (supports playlists!)
This slash command plays a link or title from a YouTube/SoundCloud song or an audio file! (supports playlists!)
Parameters
----------
source: :class:`disnake.ext.commands.ApplicationCommandInteraction`
The application command interaction
query: :class:`str` optional
The link or title
The link or YouTube/SoundCloud title
audio_file: :class:`disnake.ext.commands.Attachment` optional
The audio file to play
"""
await self.handle_play(source, query)
await self.handle_play(source, query, attachment=audio_file)

""" METHOD(S) """

async def handle_play(
self,
source: Union[Context, ApplicationCommandInteraction],
query: str = None,
*,
attachment: Attachment = None,
):
"""Searches and plays a song from a given query."""
# Get the player for this guild from cache.
Expand Down Expand Up @@ -366,14 +394,28 @@ async def handle_play(

try:
results = await player.node.get_tracks(
query if query else source.message.attachments[0].url
query if query else await source.message.attachments[0].read()
)
except TimeoutError:
return await source.reply(
f"⚠️ - {source.author.mention} - Your query timed out!",
delete_after=20,
)

if (
(not results or results and "exception" in results or not results["tracks"])
and query
and query.startswith("ytsearch:")
):
query = query.replace("ytsearch:", "scsearch:")
try:
results = await player.node.get_tracks(query)
except TimeoutError:
return await source.reply(
f"⚠️ - {source.author.mention} - Your query timed out!",
delete_after=20,
)

# Results could be None if Lavalink returns an invalid response (non-JSON/non-200 (OK)).
# ALternatively, results['tracks'] could be an empty array if the query yielded no tracks.
if results and "exception" in results:
Expand Down Expand Up @@ -402,7 +444,7 @@ async def handle_play(
# Valid loadTypes are:
# TRACK_LOADED - single video/direct URL)
# PLAYLIST_LOADED - direct URL to playlist)
# SEARCH_RESULT - query prefixed with either ytsearch: or scsearch:.
# SEARCH_RESULT - query prefixed with either ytsearch:.
# NO_MATCHES - query yielded no results
# LOAD_FAILED - most likely, the video encountered an exception during loading.
if results["loadType"] == "PLAYLIST_LOADED":
Expand All @@ -422,32 +464,80 @@ async def handle_play(
else:
content = "🎶 - **Playing:** - 🎶"

yt_infos = None
try:
if self.yt_rx.match(track["info"]["uri"]):
yt_infos = self.ytdl.extract_info(
track["info"]["uri"], download=False
)
except Exception as e:
if not f"{e}".endswith(
"This video may be inappropriate for some users."
):
if isinstance(source, Context):
return await source.reply(
f"⚠️ - {source.author.mention} - An error occurred while retrieving the video information, please try again in a few moments!",
delete_after=20,
)
else:
return await source.followup.send(
f"⚠️ - {source.author.mention} - An error occurred while retrieving the video information, please try again in a few moments!",
ephemeral=True,
)
else:
if isinstance(source, Context):
return await source.reply(
f"⚠️ - {source.author.mention} - This video is not suitable for some users! (I can't play some age restricted videos)",
delete_after=20,
)
else:
return await source.followup.send(
f"⚠️ - {source.author.mention} - This video is not suitable for some users! (I can't play some age restricted videos)",
ephemeral=True,
)

# You can attach additional information to audiotracks through kwargs, however this involves
# constructing the AudioTrack class yourself.
audio_track = AudioTrack(track, source.author.id, recommended=True)
player.add(requester=source.author.id, track=audio_track)

title = (
track["info"]["title"]
if track["info"]["title"] != "Unknown title"
or not isinstance(source, Context)
or not source.message.attachments
else source.message.attachments[0].filename
)
url = (
query
if query and not query.startswith("ytsearch")
else track["info"]["uri"]
)
duration = self.bot.utils_class.duration(track["info"]["length"] / 1000)
if yt_infos:
colour = Colour(0xFF0000)
title = yt_infos["title"]
url = (
yt_infos["webpage_url"]
if "webpage_url" in yt_infos
else yt_infos["video_url"]
)
duration = self.bot.utils_class.duration(yt_infos["duration"])
author = f"Channel: {yt_infos['channel']}"
else:
colour = self.bot.color
title = (
track["info"]["title"]
if track["info"]["title"] != "Unknown title"
or not isinstance(source, Context)
or not source.message.attachments
else source.message.attachments[0].filename
)
url = (
query
if query and not query.startswith("ytsearch")
else track["info"]["uri"]
)
duration = self.bot.utils_class.duration(track["info"]["length"] / 1000)
author = track["info"]["author"]

em = Embed(
colour=self.bot.color,
colour=colour,
title=title,
url=url,
)

em.set_author(name=track["info"]["author"])
if yt_infos is not None:
em.set_thumbnail(url=yt_infos["thumbnail"])

em.set_author(name=author)
em.set_footer(text=f"duration: {duration}")

self.bot.playlists[source.guild.id].append(
Expand All @@ -461,8 +551,9 @@ async def handle_play(
),
"title": title,
"url": url,
"author": track["info"]["author"],
"author": author,
"duration": duration,
"thumbnail": yt_infos["thumbnail"] if yt_infos is not None else None,
}
)

Expand Down
5 changes: 5 additions & 0 deletions cogs/dj/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ async def handle_playlist(
em.title = self.bot.playlists[source.guild.id][position]["title"]
em.url = self.bot.playlists[source.guild.id][position]["url"]

if self.bot.playlists[source.guild.id][position].get("thumbnail"):
em.set_thumbnail(
url=self.bot.playlists[source.guild.id][position]["thumbnail"]
)

em.set_author(
name=f"Author: {self.bot.playlists[source.guild.id][position]['author']}"
)
Expand Down
2 changes: 1 addition & 1 deletion cogs/events/on_voice_state_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ async def on_voice_state_update(
if _id in self.voice_intervals:
self.voice_intervals.pop(_id).cancel()

if not member.bot and after.channel is None:
if after.channel != before.channel:
if not [m for m in before.channel.members if not m.bot]:
if member.guild.voice_client:
await member.guild.voice_client.disconnect(force=True)
Expand Down
Binary file added lavalink/2277cf-Lavalink.jar
Binary file not shown.
8 changes: 5 additions & 3 deletions lavalink/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
FROM arm64v8/openjdk:17.0-slim-buster
# FROM openjdk:17-alpine # for amd64 CPU
# FROM arm64v8/openjdk:17.0-slim-buster
FROM openjdk:17-alpine

WORKDIR /opt/Lavalink

COPY Lavalink.jar Lavalink.jar
# temporary workaround for youtube search
# COPY Lavalink.jar Lavalink.jar
COPY 2277cf-Lavalink.jar Lavalink.jar
COPY application.yml application.yml

EXPOSE 2333
Expand Down
Loading