Skip to content

Commit

Permalink
Merge pull request #88 from Krutyi-4el/develop
Browse files Browse the repository at this point in the history
v1.9
  • Loading branch information
solaluset authored Jan 20, 2024
2 parents d5caa3c + 48402ba commit 0b32957
Show file tree
Hide file tree
Showing 20 changed files with 208 additions and 65 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: checks
on: [push, pull_request]
jobs:
run-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install pre-commit
run: pip install pre-commit

- name: Run checks
run: pre-commit run --all
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ENV VC_TIMEOUT=600
ENV VC_TIMEOUT_DEFAULT=True
ENV ALLOW_VC_TIMEOUT_EDIT=True
ENV MAX_SONG_PRELOAD=5
ENV SEARCH_RESULTS=5
ENV MAX_HISTORY_LENGTH=10
ENV MAX_TRACKNAME_HISTORY_LENGTH=15
ENV DATABASE_URL=sqlite:///settings.db
Expand All @@ -26,6 +27,7 @@ ENV EMBED_COLOR=0x4DD4D0
ENV SUPPORTED_EXTENSIONS="('.webm', '.mp4', '.mp3', '.avi', '.wav', '.m4v', '.ogg', '.mov')"
ENV COOKIE_PATH=config/cookies/cookies.txt
ENV GLOBAL_DISABLE_AUTOJOIN_VC=False
ENV ANNOUNCE_DISCONNECT=True

RUN pip --no-cache-dir install -r requirements.txt \
&& apt-get update \
Expand Down
File renamed without changes
Binary file added assets/disconnect.mp3
Binary file not shown.
File renamed without changes
File renamed without changes.
6 changes: 5 additions & 1 deletion config/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@
"--add-data=" + file + os.pathsep + "."
for file in glob.glob("config/*.json")
],
*[
"--add-data=" + file + os.pathsep + "assets"
for file in glob.glob("assets/*.mp3")
],
"-p=config",
"-n=DandelionMusic",
"-i=ui/note.ico",
"-i=assets/note.ico",
"run.py",
]
)
Expand Down
6 changes: 6 additions & 0 deletions config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class Config:

# maximum of 25
MAX_SONG_PRELOAD = 5
# how many results to display in d!search
SEARCH_RESULTS = 5

MAX_HISTORY_LENGTH = 10
MAX_TRACKNAME_HISTORY_LENGTH = 15

Expand Down Expand Up @@ -69,6 +72,9 @@ class Config:

GLOBAL_DISABLE_AUTOJOIN_VC = False

# whether to tell users the bot is disconnecting
ANNOUNCE_DISCONNECT = True

def __init__(self):
current_cfg = self.load()

Expand Down
3 changes: 3 additions & 0 deletions config/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
"HELP_MOVE_LONG": "Moves song from src_pos to dest_pos. If dest_pos is omitted, moves it to the end.",
"HELP_YT_SHORT": "Play a supported link or search on youtube",
"HELP_YT_LONG": "{prefix}p [link/video title/keywords/playlist/soundcloud link/spotify link/bandcamp link/twitter link]",
"HELP_SEARCH_SHORT": "Search song on YouTube",
"HELP_SEARCH_LONG": "Allows to choose one of {SEARCH_RESULTS} search results for a song",
"HELP_PING_SHORT": "Pong",
"HELP_PING_LONG": "Test bot response status",
"HELP_CLEAR_SHORT": "Clear the queue.",
Expand All @@ -75,6 +77,7 @@
"HELP_REMOVE_LONG": "Allows to remove a song from the queue by typing it's position (defaults to the last song).",

"SETTINGS_EMOJI_CHECK_MSG": "Checking the emoji...",
"SEARCH_EMBED_TITLE": "Results:",

"PauseState": {
"NOTHING_TO_PAUSE": "Nothing to pause or resume.",
Expand Down
3 changes: 2 additions & 1 deletion config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def get_env_var(key: str, default: T) -> T:
value = ast.literal_eval(value)
except (SyntaxError, ValueError):
pass
assert type(value) is type(default), f"invalid value for {key}: {value!r}"
if type(value) is not type(default):
raise TypeError(f"invalid value for {key}: {value!r}")
return value


Expand Down
47 changes: 31 additions & 16 deletions musicbot/audiocontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import asyncio
from itertools import islice
from inspect import isawaitable
from traceback import print_exc
from typing import TYPE_CHECKING, Coroutine, Optional

import discord
Expand All @@ -10,7 +11,7 @@
from musicbot import linkutils, utils, loader
from musicbot.playlist import Playlist, LoopMode, LoopState, PauseState
from musicbot.songinfo import Song
from musicbot.utils import CheckError, play_check
from musicbot.utils import CheckError, asset, play_check

# avoiding circular import
if TYPE_CHECKING:
Expand All @@ -22,14 +23,15 @@


class MusicButton(discord.ui.Button):
def __init__(self, callback, **kwargs):
def __init__(self, callback, check=play_check, **kwargs):
super().__init__(**kwargs)
self._callback = callback
self._check = check

async def callback(self, inter):
ctx = await inter.client.get_application_context(inter)
try:
await play_check(ctx)
await self._check(ctx)
except CheckError as e:
await ctx.send(e, ephemeral=True)
return
Expand Down Expand Up @@ -81,8 +83,11 @@ def volume(self, value: int):
self._volume = value
try:
self.guild.voice_client.source.volume = float(value) / 100.0
except Exception:
except AttributeError:
pass
except Exception:
print("Unknown error when setting volume:", file=sys.stderr)
print_exc(file=sys.stderr)

def volume_up(self):
self.volume = min(self.volume + 10, 100)
Expand Down Expand Up @@ -221,7 +226,8 @@ async def update_view(self, view=_not_provided):
except discord.NotFound:
self.last_message = None
else:
print("Failed to update view:", e, file=sys.stderr)
print("Failed to update view:", file=sys.stderr)
print_exc(file=sys.stderr)

def is_active(self) -> bool:
client = self.guild.voice_client
Expand Down Expand Up @@ -322,21 +328,19 @@ async def play_song(self, song: Song):
self.current_song = song

self.guild.voice_client.play(
discord.FFmpegPCMAudio(
song.base_url,
before_options="-reconnect 1 -reconnect_streamed 1"
" -reconnect_delay_max 5",
options="-loglevel error",
stderr=sys.stderr,
discord.PCMVolumeTransformer(
discord.FFmpegPCMAudio(
song.base_url,
before_options="-reconnect 1 -reconnect_streamed 1"
" -reconnect_delay_max 5",
options="-loglevel error",
stderr=sys.stderr,
),
float(self.volume) / 100.0,
),
after=self.next_song,
)

self.guild.voice_client.source = discord.PCMVolumeTransformer(
self.guild.voice_client.source
)
self.guild.voice_client.source.volume = float(self.volume) / 100.0

if (
self.bot.settings[self.guild].announce_songs
and self.command_channel
Expand Down Expand Up @@ -450,6 +454,17 @@ async def udisconnect(self):
await self.update_view(None)
if self.guild.voice_client is None:
return False
if config.ANNOUNCE_DISCONNECT:
try:
await self.guild.voice_client.play(
discord.FFmpegPCMAudio(asset("disconnect.mp3")),
wait_finish=True,
)
except Exception:
print_exc(file=sys.stderr)
else:
# let it finish
await asyncio.sleep(1)
await self.guild.voice_client.disconnect(force=True)
self.timer.cancel()
return True
2 changes: 2 additions & 0 deletions musicbot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ async def start(self, *args, **kwargs):
return await super().start(*args, **kwargs)

async def close(self):
print("Shutting down...", flush=True)

await asyncio.gather(
*(
audiocontroller.udisconnect()
Expand Down
7 changes: 5 additions & 2 deletions musicbot/commands/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ def __init__(self, bot: MusicBot):
)
@commands.check(voice_check)
async def _connect(self, ctx: Context):
audiocontroller = ctx.bot.audio_controllers[ctx.guild]
await audiocontroller.uconnect(ctx, move=True)
# connect only if not connected yet
if not ctx.guild.voice_client:
audiocontroller = ctx.bot.audio_controllers[ctx.guild]
await audiocontroller.uconnect(ctx, move=True)
await ctx.send("Connected.")

@bridge.bridge_command(
Expand All @@ -41,6 +43,7 @@ async def _connect(self, ctx: Context):
)
@commands.check(voice_check)
async def _disconnect(self, ctx: Context):
await ctx.defer() # ANNOUNCE_DISCONNECT will take a while
audiocontroller = ctx.bot.audio_controllers[ctx.guild]
if await audiocontroller.udisconnect():
await ctx.send("Disconnected.")
Expand Down
52 changes: 49 additions & 3 deletions musicbot/commands/music.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
from discord import Option, Attachment
from discord.ui import View
from discord.ext import commands, bridge

from config import config
from musicbot import linkutils, utils
from musicbot.loader import SongError
from musicbot.songinfo import Song
from musicbot.bot import MusicBot, Context
from musicbot.audiocontroller import AudioController
from musicbot.audiocontroller import AudioController, MusicButton
from musicbot.loader import SongError, search_youtube
from musicbot.playlist import PlaylistError, LoopMode


class AudioContext(Context):
audiocontroller: AudioController


class SongButton(MusicButton):
def __init__(self, cog: "Music", num: int, song: str):
async def play(ctx):
view = self.view
view.stop()
view.disable_all_items()
async with ctx.channel.typing():
await view.message.edit(view=view)
await cog._play_song(ctx, song)

super().__init__(play, cog.cog_check, emoji=f"{num}⃣")


@commands.check
def active_only(ctx: AudioContext):
if not ctx.audiocontroller.is_active():
Expand Down Expand Up @@ -43,7 +58,7 @@ async def cog_before_invoke(self, ctx: AudioContext):
help=config.HELP_YT_SHORT,
aliases=["p", "yt", "pl"],
)
async def _play_song(
async def _play(
self, ctx: AudioContext, *, track: str = None, file: Attachment = None
):
if ctx.message and ctx.message.attachments:
Expand All @@ -55,7 +70,9 @@ async def _play_song(
return

await ctx.defer()
await self._play_song(ctx, track)

async def _play_song(self, ctx: AudioContext, track: str):
# reset timer
await ctx.audiocontroller.timer.start(True)

Expand All @@ -81,6 +98,35 @@ async def _play_song(
embed=song.info.format_output(config.SONGINFO_NOW_PLAYING)
)

@bridge.bridge_command(
name="search",
description=config.HELP_SEARCH_LONG,
help=config.HELP_SEARCH_SHORT,
aliases=["sc"],
)
async def _search(self, ctx: AudioContext, *, query: str):
await ctx.defer()
results = await search_youtube(query, config.SEARCH_RESULTS)
songs = []
for data in results:
song = Song(
linkutils.Origins.Default,
linkutils.SiteTypes.YT_DLP,
webpage_url=data["url"],
)
song.update(data)
songs.append(song)

await ctx.send(
embed=utils.songs_embed(config.SEARCH_EMBED_TITLE, songs),
view=View(
*(
SongButton(self, i, data["url"])
for i, data in enumerate(results, start=1)
)
),
)

@bridge.bridge_command(
name="loop",
description=config.HELP_LOOP_LONG,
Expand Down
13 changes: 7 additions & 6 deletions musicbot/linkutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@
# https://gist.github.com/gruber/249502#gistcomment-1328838
url_regex = re.compile(
r"(?i)\b((?:[a-z][\w.+-]+:(?:/{1,3}|[?+]?[a-z0-9%]))"
r"(?:[^\s()<>]|\((?:[^\s()<>]|(?:\([^\s()<>]+\)))*\))+"
r"(?P<bare>(?:[^\s()<>]|\((?:[^\s()<>]|(?:\([^\s()<>]+\)))*\))+"
r"(?:\((?:[^\s()<>]|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'"
r'"'
r".,<>?«»“”‘’]))"
r".,<>?«»“”‘’])))"
)
spotify_regex = re.compile(
r"^https?://open\.spotify\.com/([^/]+/)?"
Expand Down Expand Up @@ -88,6 +88,7 @@ class SiteTypes(Enum):
YT_DLP = auto()
CUSTOM = auto()
UNKNOWN = auto()
NOT_URL = auto()


class SpotifyPlaylistTypes(Enum):
Expand Down Expand Up @@ -179,7 +180,7 @@ def fetch_playlist_with_api(


def get_urls(content: str) -> List[str]:
return url_regex.findall(content)
return [m[0] for m in url_regex.findall(content)]


def get_ie(url: str) -> Optional[ExtractorT]:
Expand All @@ -189,9 +190,9 @@ def get_ie(url: str) -> Optional[ExtractorT]:
return None


def identify_url(url: Optional[str]) -> Union[SiteTypes, ExtractorT]:
if url is None or not url_regex.fullmatch(url):
return SiteTypes.UNKNOWN
def identify_url(url: str) -> Union[SiteTypes, ExtractorT]:
if not url_regex.fullmatch(url):
return SiteTypes.NOT_URL

if spotify_regex.match(url):
return SiteTypes.SPOTIFY
Expand Down
Loading

0 comments on commit 0b32957

Please sign in to comment.