diff --git a/royalnet/audio/playmodes.py b/royalnet/audio/playmodes.py index fad8a667..5028950d 100644 --- a/royalnet/audio/playmodes.py +++ b/royalnet/audio/playmodes.py @@ -1,6 +1,7 @@ import math import random import typing +from collections import namedtuple from .ytdldiscord import YtdlDiscord from .fileaudiosource import FileAudioSource @@ -104,7 +105,7 @@ class Playlist(PlayMode): class Pool(PlayMode): - """A :py:class:`royalnet.audio.RoyalPCMAudio` pool. :py:class:`royalnet.audio.RoyalPCMAudio` are selected in random order and are not repeated until every song has been played at least once.""" + """A random pool. :py:class:`royalnet.audio.YtdlDiscord` are selected in random order and are not repeated until every song has been played at least once.""" def __init__(self, starting_pool: typing.List[YtdlDiscord] = None): """Create a new Pool. @@ -148,3 +149,65 @@ class Pool(PlayMode): preview_pool = self.pool.copy() random.shuffle(preview_pool) return preview_pool + + +class Layers(PlayMode): + """A playmode for playing a single song with multiple layers.""" + + Layer = namedtuple("Layer", ["dfile", "source"]) + + def __init__(self, starting_layers: typing.List[YtdlDiscord] = None): + super().__init__() + if starting_layers is None: + starting_layers = [] + self.layers = [] + for item in starting_layers: + self.add(item) + + def videos_left(self) -> typing.Union[int, float]: + return 1 if len(self.layers) > 0 else 0 + + async def _generate_generator(self): + current_layer = None + current_source = None + while True: + if len(self.layers) == 0: + yield None + continue + if self.now_playing is None: + self.now_playing = self.layers[0].dfile + current_source = self.layers[0].source + current_layer = 0 + yield current_source + continue + if current_source.file.closed: + self.now_playing = None + self.layers = [] + current_layer = None + current_source = None + yield None + continue + current_layer += 1 + current_position = current_source.file.tell() + if current_layer >= len(self.layers): + self.now_playing = self.layers[0].dfile + current_source = self.layers[0].source + current_source.file.seek(current_position) + current_layer = 0 + yield current_source + continue + self.now_playing = self.layers[current_layer].dfile + current_source = self.layers[current_layer].source + current_source.file.seek(current_position) + yield current_source + + def add(self, item) -> None: + self.layers.append(self.Layer(dfile=item, source=item.spawn_audiosource())) + + def delete(self) -> None: + for item in self.layers: + item.dfile.delete() + self.layers = None + + def queue_preview(self) -> typing.List[YtdlDiscord]: + return [layer.dfile for layer in self.layers] diff --git a/royalnet/audio/ytdldiscord.py b/royalnet/audio/ytdldiscord.py index 1b171b75..db31074b 100644 --- a/royalnet/audio/ytdldiscord.py +++ b/royalnet/audio/ytdldiscord.py @@ -1,5 +1,4 @@ import typing -import discord import re import ffmpeg import os @@ -37,7 +36,7 @@ class YtdlDiscord: if not self.pcm_available(): self.convert_to_pcm() - def spawn_audiosource(self) -> discord.AudioSource: + def spawn_audiosource(self) -> FileAudioSource: if not self.pcm_available(): raise FileNotFoundError("File hasn't been converted to PCM yet") stream = open(self.pcm_filename, "rb") diff --git a/royalnet/commands/playmode.py b/royalnet/commands/playmode.py index 10fb84d7..f562bd5a 100644 --- a/royalnet/commands/playmode.py +++ b/royalnet/commands/playmode.py @@ -2,7 +2,7 @@ import typing from ..utils import Command, Call, NetworkHandler from ..network import Request, ResponseSuccess from ..error import NoneFoundError, TooManyFoundError -from ..audio.playmodes import Playlist, Pool +from ..audio.playmodes import Playlist, Pool, Layers if typing.TYPE_CHECKING: from ..bots import DiscordBot @@ -30,6 +30,8 @@ class PlaymodeNH(NetworkHandler): bot.music_data[guild] = Playlist() elif data["mode_name"] == "pool": bot.music_data[guild] = Pool() + elif data["mode_name"] == "layers": + bot.music_data[guild] = Layers() else: raise ValueError("No such PlayMode") return ResponseSuccess() diff --git a/royalnet/commands/queue.py b/royalnet/commands/queue.py index b2557604..adba83a2 100644 --- a/royalnet/commands/queue.py +++ b/royalnet/commands/queue.py @@ -71,6 +71,11 @@ class QueueCommand(Command): message = f"ℹ️ Questo [c]Pool[/c] è vuoto." else: message = f"ℹ️ Questo [c]Pool[/c] contiene {len(data['queue']['strings'])} elementi, tra cui:\n" + elif data["type"] == "Layers": + if len(data["queue"]["strings"]) == 0: + message = f"ℹ️ Nessun elemento è attualmente in riproduzione, pertanto non ci sono [c]Layers[/c]:" + else: + message = f"ℹ️ I [c]Layers[/c] dell'elemento attualmente in riproduzione sono {len(data['queue']['strings'])}, tra cui:\n" else: if len(data["queue"]["strings"]) == 0: message = f"ℹ️ Il PlayMode attuale, [c]{data['type']}[/c], è vuoto.\n"