From 9e129e7ad0db9ef8284c503b0305781b7dcb9eee Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sun, 29 Mar 2020 21:19:58 +0200 Subject: [PATCH] Add lazy music commands --- royalpack/commands/__init__.py | 16 ++++ royalpack/commands/lazyfunkwhale.py | 27 +++++++ royalpack/commands/lazyfunkwhaleplaylist.py | 32 ++++++++ royalpack/commands/lazygooglevideo.py | 16 ++++ royalpack/commands/lazypeertube.py | 24 ++++++ royalpack/commands/lazyplay.py | 62 +++++++++++++++ royalpack/commands/lazysoundcloud.py | 14 ++++ royalpack/commands/lazyyahoovideo.py | 16 ++++ royalpack/commands/lazyyoutube.py | 14 ++++ royalpack/commands/peertube.py | 4 +- royalpack/events/__init__.py | 2 + royalpack/events/discord_lazy_play.py | 88 +++++++++++++++++++++ 12 files changed, 313 insertions(+), 2 deletions(-) create mode 100644 royalpack/commands/lazyfunkwhale.py create mode 100644 royalpack/commands/lazyfunkwhaleplaylist.py create mode 100644 royalpack/commands/lazygooglevideo.py create mode 100644 royalpack/commands/lazypeertube.py create mode 100644 royalpack/commands/lazyplay.py create mode 100644 royalpack/commands/lazysoundcloud.py create mode 100644 royalpack/commands/lazyyahoovideo.py create mode 100644 royalpack/commands/lazyyoutube.py create mode 100644 royalpack/events/discord_lazy_play.py diff --git a/royalpack/commands/__init__.py b/royalpack/commands/__init__.py index 317dbe6a..b96c3acd 100644 --- a/royalpack/commands/__init__.py +++ b/royalpack/commands/__init__.py @@ -40,6 +40,14 @@ from .diarioshuffle import DiarioshuffleCommand from .funkwhaleplaylist import FunkwhaleplaylistCommand from .voicestatus import VoicestatusCommand from .playmode import PlaymodeCommand +from .lazyplay import LazyplayCommand +from .lazyfunkwhale import LazyfunkwhaleCommand +from .lazyfunkwhaleplaylist import LazyfunkwhaleplaylistCommand +from .lazygooglevideo import LazygooglevideoCommand +from .lazypeertube import LazypeertubeCommand +from .lazysoundcloud import LazysoundcloudCommand +from .lazyyahoovideo import LazyyahoovideoCommand +from .lazyyoutube import LazyyoutubeCommand # Enter the commands of your Pack here! available_commands = [ @@ -84,6 +92,14 @@ available_commands = [ FunkwhaleplaylistCommand, VoicestatusCommand, PlaymodeCommand, + LazyplayCommand, + LazyfunkwhaleCommand, + LazyfunkwhaleplaylistCommand, + LazygooglevideoCommand, + LazypeertubeCommand, + LazysoundcloudCommand, + LazyyahoovideoCommand, + LazyyoutubeCommand, ] # Don't change this, it should automatically generate __all__ diff --git a/royalpack/commands/lazyfunkwhale.py b/royalpack/commands/lazyfunkwhale.py new file mode 100644 index 00000000..3b6d839e --- /dev/null +++ b/royalpack/commands/lazyfunkwhale.py @@ -0,0 +1,27 @@ +from .lazyplay import LazyplayCommand +from royalnet.commands import * +import aiohttp +import urllib.parse + + +class LazyfunkwhaleCommand(LazyplayCommand): + name: str = "lazyfunkwhale" + + aliases = ["lazyfuckwhale", "lfw", "lazyroyalwhale", "lrw"] + + description: str = "Cerca una canzone su RoyalWhale e aggiungila (lazy) alla coda della chat vocale." + + syntax = "{ricerca}" + + def get_embed_color(self): + return 0x009FE3 + + async def get_urls(self, args): + search = urllib.parse.quote(args.joined(require_at_least=1)) + async with aiohttp.ClientSession() as session: + async with session.get(self.config["Funkwhale"]["instance_url"] + + f"/api/v1/search?query={search}") as response: + j = await response.json() + if len(j["tracks"]) < 1: + raise UserError("Nessun file audio trovato con il nome richiesto.") + return [f'{self.config["Funkwhale"]["instance_url"]}{j["tracks"][0]["listen_url"]}'] diff --git a/royalpack/commands/lazyfunkwhaleplaylist.py b/royalpack/commands/lazyfunkwhaleplaylist.py new file mode 100644 index 00000000..8d6bf0e0 --- /dev/null +++ b/royalpack/commands/lazyfunkwhaleplaylist.py @@ -0,0 +1,32 @@ +from .lazyplay import LazyplayCommand +from royalnet.commands import * +import aiohttp +import urllib.parse + + +class LazyfunkwhaleplaylistCommand(LazyplayCommand): + name: str = "lazyfunkwhaleplaylist" + + aliases = ["lfwp", "lfwplaylist", "lazyfunkwhalep"] + + description: str = "Cerca una playlist su RoyalWhale e aggiungila (lazy) alla coda della chat vocale." + + syntax = "{ricerca}" + + def get_embed_color(self): + return 0x009FE3 + + async def get_urls(self, args): + search = urllib.parse.quote(args.joined(require_at_least=1)) + async with aiohttp.ClientSession() as session: + async with session.get(self.config["Funkwhale"]["instance_url"] + + f"/api/v1/playlists/?q={search}&ordering=-creation_date&playable=true") as response: + j = await response.json() + if len(j["results"]) < 1: + raise UserError("Nessuna playlist trovata con il nome richiesto.") + playlist = j["results"][0] + playlist_id = playlist["id"] + async with session.get(self.config["Funkwhale"]["instance_url"] + + f"/api/v1/playlists/{playlist_id}/tracks") as response: + j = await response.json() + return list(map(lambda t: f'{self.config["Funkwhale"]["instance_url"]}{t["track"]["listen_url"]}', j["results"])) diff --git a/royalpack/commands/lazygooglevideo.py b/royalpack/commands/lazygooglevideo.py new file mode 100644 index 00000000..24ae7183 --- /dev/null +++ b/royalpack/commands/lazygooglevideo.py @@ -0,0 +1,16 @@ +from .lazyplay import LazyplayCommand + + +class LazygooglevideoCommand(LazyplayCommand): + name: str = "lazygooglevideo" + + aliases = ["lgv"] + + description: str = "Cerca un video su Google Video e lo aggiunge (lazy) alla coda della chat vocale." + + syntax = "{ricerca}" + + async def get_urls(self, args): + return [f"gvsearch:{args.joined()}"] + + # Too bad gvsearch: always finds nothing. diff --git a/royalpack/commands/lazypeertube.py b/royalpack/commands/lazypeertube.py new file mode 100644 index 00000000..f3668a04 --- /dev/null +++ b/royalpack/commands/lazypeertube.py @@ -0,0 +1,24 @@ +from .lazyplay import LazyplayCommand +from royalnet.commands import * +import aiohttp +import urllib.parse + + +class LazypeertubeCommand(LazyplayCommand): + name: str = "lazypeertube" + + aliases = ["lpt", "lazyroyaltube", "lrt"] + + description: str = "Cerca un video su RoyalTube e lo aggiunge (lazy) alla coda della chat vocale." + + syntax = "{ricerca}" + + async def get_urls(self, args): + search = urllib.parse.quote(args.joined(require_at_least=1)) + async with aiohttp.ClientSession() as session: + async with session.get(self.config["Peertube"]["instance_url"] + + f"/api/v1/search/videos?search={search}") as response: + j = await response.json() + if j["total"] < 1: + raise InvalidInputError("Nessun video trovato.") + return [f'{self.config["Peertube"]["instance_url"]}/videos/watch/{j["data"][0]["uuid"]}'] diff --git a/royalpack/commands/lazyplay.py b/royalpack/commands/lazyplay.py new file mode 100644 index 00000000..795c4d5e --- /dev/null +++ b/royalpack/commands/lazyplay.py @@ -0,0 +1,62 @@ +import discord +import asyncio as aio +from typing import * +from royalnet.commands import * +from royalnet.backpack.tables import User, Discord + + +class LazyplayCommand(Command): + name: str = "lazyplay" + + aliases = ["lp"] + + description: str = "Aggiunge un url alla coda della chat vocale, ma lo scarica solo quando sta per essere" \ + " riprodotto." + + syntax = "{url}" + + async def get_urls(self, args: CommandArgs): + url = args.joined(require_at_least=1) + if not (url.startswith("http://") or url.startswith("https://")): + raise InvalidInputError(f"L'URL specificato non inizia con il nome di un protocollo supportato" + f" ([c]http://[/c] o [c]https://[/c]).") + return [url] + + def get_embed_color(self) -> Optional[int]: + return None + + async def run(self, args: CommandArgs, data: CommandData) -> None: + if self.interface.name == "discord": + message: discord.Message = data.message + guild: discord.Guild = message.guild + if guild is None: + guild_id = None + else: + guild_id: Optional[int] = guild.id + else: + guild_id = None + + user: User = await data.get_author() + user_str = None + + if user is not None: + try: + user_discord: Discord = user.discord[0] + except (AttributeError, IndexError): + user_str = str(user) + else: + user_str = str(f"<@{user_discord.discord_id}>") + + urls = await self.get_urls(args) + + play_task: aio.Task = self.loop.create_task( + self.interface.call_herald_event("discord", "discord_lazy_play", + urls=urls, + guild_id=guild_id, + user=user_str, + force_color=self.get_embed_color()) + ) + + await data.reply("⌛ Attendi un attimo...") + + await play_task diff --git a/royalpack/commands/lazysoundcloud.py b/royalpack/commands/lazysoundcloud.py new file mode 100644 index 00000000..e3e181c5 --- /dev/null +++ b/royalpack/commands/lazysoundcloud.py @@ -0,0 +1,14 @@ +from .lazyplay import LazyplayCommand + + +class LazysoundcloudCommand(LazyplayCommand): + name: str = "lazysoundcloud" + + aliases = ["lsc"] + + description: str = "Cerca un video su SoundCloud e lo aggiunge (lazy) alla coda della chat vocale." + + syntax = "{ricerca}" + + async def get_urls(self, args): + return [f"scsearch:{args.joined()}"] diff --git a/royalpack/commands/lazyyahoovideo.py b/royalpack/commands/lazyyahoovideo.py new file mode 100644 index 00000000..f0f4f1d5 --- /dev/null +++ b/royalpack/commands/lazyyahoovideo.py @@ -0,0 +1,16 @@ +from .lazyplay import LazyplayCommand + + +class LazyyahoovideoCommand(LazyplayCommand): + name: str = "lazyyahoovideo" + + aliases = ["lyv"] + + description: str = "Cerca un video su Yahoo Video e lo aggiunge (lazy) alla coda della chat vocale." + + syntax = "{ricerca}" + + async def get_urls(self, args): + return [f"yvsearch:{args.joined()}"] + + # Too bad yvsearch: always finds nothing. diff --git a/royalpack/commands/lazyyoutube.py b/royalpack/commands/lazyyoutube.py new file mode 100644 index 00000000..cf6a56b9 --- /dev/null +++ b/royalpack/commands/lazyyoutube.py @@ -0,0 +1,14 @@ +from .lazyplay import LazyplayCommand + + +class LazyyoutubeCommand(LazyplayCommand): + name: str = "lazyyoutube" + + aliases = ["lyt"] + + description: str = "Cerca un video su YouTube e lo aggiunge (lazy) alla coda della chat vocale." + + syntax = "{ricerca}" + + async def get_urls(self, args): + return [f"ytsearch:{args.joined()}"] diff --git a/royalpack/commands/peertube.py b/royalpack/commands/peertube.py index 9a8648dc..51b08fd8 100644 --- a/royalpack/commands/peertube.py +++ b/royalpack/commands/peertube.py @@ -13,7 +13,7 @@ class PeertubeCommand(PlayCommand): syntax = "{ricerca}" - async def get_url(self, args): + async def get_urls(self, args): search = urllib.parse.quote(args.joined(require_at_least=1)) async with aiohttp.ClientSession() as session: async with session.get(self.config["Peertube"]["instance_url"] + @@ -21,4 +21,4 @@ class PeertubeCommand(PlayCommand): j = await response.json() if j["total"] < 1: raise InvalidInputError("Nessun video trovato.") - return f'{self.config["Peertube"]["instance_url"]}/videos/watch/{j["data"][0]["uuid"]}' + return [f'{self.config["Peertube"]["instance_url"]}/videos/watch/{j["data"][0]["uuid"]}'] diff --git a/royalpack/events/__init__.py b/royalpack/events/__init__.py index f956ee21..6637fbe2 100644 --- a/royalpack/events/__init__.py +++ b/royalpack/events/__init__.py @@ -6,6 +6,7 @@ from .discord_skip import DiscordSkipEvent from .discord_queue import DiscordQueueEvent from .discord_pause import DiscordPauseEvent from .discord_playable import DiscordPlaymodeEvent +from .discord_lazy_play import DiscordLazyPlayEvent # Enter the commands of your Pack here! available_events = [ @@ -16,6 +17,7 @@ available_events = [ DiscordQueueEvent, DiscordPauseEvent, DiscordPlaymodeEvent, + DiscordLazyPlayEvent, ] # Don't change this, it should automatically generate __all__ diff --git a/royalpack/events/discord_lazy_play.py b/royalpack/events/discord_lazy_play.py new file mode 100644 index 00000000..8b76a4ff --- /dev/null +++ b/royalpack/events/discord_lazy_play.py @@ -0,0 +1,88 @@ +import datetime +from typing import * + +import discord +import royalnet.commands as rc +import royalnet.serf.discord as rsd +import royalnet.bard.discord as rbd + +from ..utils import RoyalQueue + + +class DiscordLazyPlayEvent(rc.Event): + name = "discord_lazy_play" + + async def run(self, + urls: List[str], + guild_id: Optional[int] = None, + user: Optional[str] = None, + force_color: Optional[int] = None, + **kwargs) -> dict: + if not isinstance(self.serf, rsd.DiscordSerf): + raise rc.UnsupportedError() + + serf: rsd.DiscordSerf = self.serf + client: discord.Client = self.serf.client + guild: discord.Guild = client.get_guild(guild_id) if guild_id is not None else None + candidate_players: List[rsd.VoicePlayer] = serf.find_voice_players(guild) + + if len(candidate_players) == 0: + raise rc.UserError("Il bot non è in nessun canale vocale.\n" + "Evocalo prima con [c]summon[/c]!") + elif len(candidate_players) == 1: + voice_player = candidate_players[0] + else: + raise rc.CommandError("Non so in che Server riprodurre questo file...\n" + "Invia il comando su Discord, per favore!") + + added: List[rbd.YtdlDiscord] = [] + too_long: List[rbd.YtdlDiscord] = [] + + for url in urls: + ytds = await rbd.YtdlDiscord.from_url(url) + if isinstance(voice_player.playing, RoyalQueue): + for index, ytd in enumerate(ytds): + if ytd.info.duration >= datetime.timedelta(seconds=self.config["Play"]["max_song_duration"]): + too_long.append(ytd) + continue + added.append(ytd) + voice_player.playing.contents.append(ytd) + if not voice_player.voice_client.is_playing(): + await voice_player.start() + else: + raise rc.CommandError(f"Non so come aggiungere musica a [c]{voice_player.playing.__class__.__qualname__}[/c]!") + + main_channel: discord.TextChannel = client.get_channel(self.config["Discord"]["main_channel_id"]) + + if len(added) > 0: + if user: + await main_channel.send(rsd.escape(f"▶️ {user} ha aggiunto {len(added)} file _(lazy)_ alla coda:")) + else: + await main_channel.send(rsd.escape(f"▶️ Aggiunt{'o' if len(added) == 1 else 'i'} {len(added)} file " + f"_(lazy)_ alla coda:")) + for ytd in added: + embed: discord.Embed = ytd.embed() + if force_color: + embed._colour = discord.Colour(force_color) + await main_channel.send(embed=embed) + + if len(too_long) > 0: + if user: + await main_channel.send(rsd.escape( + f"⚠ {len(too_long)} file non {'è' if len(too_long) == 1 else 'sono'}" + f" stat{'o' if len(too_long) == 1 else 'i'} scaricat{'o' if len(too_long) == 1 else 'i'}" + f" perchè durava{'' if len(too_long) == 1 else 'no'}" + f" più di [c]{self.config['Play']['max_song_duration']}[/c] secondi." + )) + + if len(added) + len(too_long) == 0: + raise rc.InvalidInputError("Non è stato aggiunto nessun file alla coda.") + + return { + "added": [{ + "title": ytd.info.title, + } for ytd in added], + "too_long": [{ + "title": ytd.info.title, + } for ytd in too_long] + }