Discord Bot mit Aufnahmefunktion ab Python: (4) Musikdateien abspielen

Einführung

Dieser Artikel ist eine Fortsetzung des vorherigen Discord Bot mit Aufnahmefunktion beginnend mit Python: (3) Zusammenarbeit mit der Datenbank.

Dieses Mal werden wir eine Jukebox-Funktion hinzufügen, die die zuvor auf dem Server vorbereitete Musik mit einem Befehl wiedergibt. Wenn Sie discord.py verwenden, können Sie Audio mit einem sehr einfachen Vorgang abspielen. Hier fügen wir eine Funktion hinzu, die eine kontinuierliche Wiedergabe ermöglicht, indem eine Song-Warteschlange implementiert wird, anstatt sie einfach abzuspielen.

Wir planen insgesamt 7 Mal zu schreiben und haben bis zu 5 Artikel fertig geschrieben.

  1. Discord Bot mit Aufnahmefunktion beginnend mit Python: (1) Erste Schritte discord.py
  2. Discord Bot mit Aufnahmefunktion beginnend mit Python: (2) Praktische Funktion (Bot-Erweiterung, Cog, Embed)
  3. Discord Bot mit Aufnahmefunktion beginnend mit Python: (3) Zusammenarbeit mit der Datenbank
  4. Discord Bot mit Aufnahmefunktion beginnend mit Python: (4) Musikdateien abspielen
  5. Discord Bot mit Aufnahmefunktion ab Python: (5) Discord API direkt bedienen

Zwietracht Sprachkommunikation

In discord.py handelt es sich um einen Teil, der nicht direkt manipuliert wird, sodass nicht viel zu beachten ist, sondern zusätzlich zur REST-API, die beim Senden von Posts mit HTTP-Anforderungen und beim Abrufen von Serverinformationen bidirektional verwendet wird Es gibt drei Methoden zum Senden und Empfangen von Informationen: WebSocket-Kommunikation für die Kommunikation und RTP-Kommunikation zum Senden und Empfangen von Sprache.

Im Fall von discord.py ist es ein sehr intuitiver Mechanismus, eine Instanz des Sprachkanals aus dem Kontext usw. abzurufen und eine Verbindung zum Sprachkanal herzustellen, indem das Collout "Verbinden" dieser Instanz ausgeführt wird. Was von diesem connect zurückgegeben wird, ist eine Instanz der VoiceClient -Klasse.

In "VoiceClient" sind die oben genannten Teile wie WebSocket-Kommunikation und andere verschlüsselte Kommunikation ausgeblendet. Wenn Sie die Sprachkanalinformationen für jeden Server bearbeiten möchten, können Sie daher verschiedene Vorgänge für diese VoiceClient-Instanz ausführen.

Wenn Sie die Verarbeitung von Sprachkanälen von mehreren Servern in Betracht ziehen, ist ein Mechanismus zum Verbinden dieser Instanz mit dem Server (ID) erforderlich. Hier betreiben wir VoiceClient mit einem Wörterbucharray für die Verwaltung wie Offizielle Implementierung im discord.py-Repository. Versuchen Sie dann, die Musik lokal abzuspielen (auf dem Server, auf dem der Bot ausgeführt wird).

VoiceChannel bedienen

Bis Sie den Sprachkanal betreten

Wie oben erwähnt, ist das Betreten und Verlassen des Sprachkanals sehr einfach. Hier erstellen wir ein Zahnrad namens "Voice", betreten den Raum mit "$ join" und gehen mit "$ Leave". Ein Beispiel für die Implementierung ist wie folgt.

python:./src/app/dbot/cogs/Voice.py


import discord
from discord.ext import commands
from typing import Dict
from dbot.core.bot import DBot


class Voice(commands.Cog):
    def __init__(self, bot: DBot):
        self.bot = bot
        self.voice_clients: Dict[int, discord.VoiceClient] = {}

    @commands.command()
    async def join(self, ctx: commands.Context):
        #Nicht am Sprachkanal teilnehmen
        if not ctx.author.voice or not ctx.author.voice.channel:
            return await ctx.send('Treten Sie zuerst dem Sprachkanal bei')
        vc = await ctx.author.voice.channel.connect()
        self.voice_clients[ctx.guild.id] = vc

    @commands.command()
    async def leave(self, ctx: commands.Context):
        vc = self.voice_clients.get(ctx.guild.id)
        if vc is None:
            return await ctx.send('Ich bin dem Sprachkanal noch nicht beigetreten')
        await vc.disconnect()
        del self.voice_clients[ctx.guild.id]


def setup(bot):
    return bot.add_cog(Voice(bot))

Die Eigenschaft voice von discord.Member gibt eine Instanz der VoiceState-Klasse zurück, wenn das Mitglied einem Sprachkanal angeschlossen ist. Dieser VoiceState verfügt zusätzlich zu dem Status, z. B. Stummschaltung des Mitglieds, über eine Eigenschaft namens "channel". Unter Bezugnahme darauf wird eine Instanz der Sprachkanalklasse (discord.VoiceChannel) abgerufen, die sich derzeit im Member befindet. tun können.

Das Aufrufen des "Connect" -Kollouts von "VoiceChannel" gibt "VoiceClient" zurück und der Bot tritt dem Sprachkanal bei. Der VoiceClient wird im Wörterbuch mit der Server-ID als Schlüssel gespeichert. Im Gegenteil, "$ Leave" sucht in der Server-ID nach "VoiceClient" und ruft das Collout "Disconnect" auf. Der Ein- / Ausstiegsprozess ist nun abgeschlossen.

Audio abspielen

Bereiten Sie zunächst die Musik für die Wiedergabe vor und speichern Sie sie im Ordner . / Src / app / music /.

Image from Gyazo

Geben Sie ihm einen geeigneten Namen, um es zu implementieren, damit es anhand des Namens sucht. Spielen Sie vorerst mit "$ play song title" und hören Sie mit "$ stop" auf.

python:./src/app/dbot/cogs/Voice.py


import discord
from glob import glob
import os
from discord.ext import commands
from typing import Dict
from dbot.core.bot import DBot


class Voice(commands.Cog):
    #Abkürzung

    @commands.command()
    async def play(self, ctx: commands.Context, *, title: str = ''):
        vc: discord.VoiceClient = self.voice_clients.get(ctx.guild.id)
        if vc is None:
            await ctx.invoke(self.join)
            vc = self.voice_clients[ctx.guild.id]
        music_pathes = glob('./music/**.mp3')
        music_titles = [
            os.path.basename(path).rstrip('.mp3')
            for path in music_pathes
        ]
        if not title in music_titles:
            return await ctx.send('Es gibt kein bestimmtes Lied.')
        idx = music_titles.index(title)
        src = discord.FFmpegPCMAudio(music_pathes[idx])
        vc.play(src)
        await ctx.send(f'{title}Spielen')

    @commands.command()
    async def stop(self, ctx: commands.Context):
        vc: discord.VoiceClient = self.voice_clients.get(ctx.guild.id)
        if vc is None:
            return await ctx.send('Bot ist dem Sprachkanal noch nicht beigetreten')
        if not vc.is_playing:
            return await ctx.send('Schon gestoppt')
        await vc.stop()
        await ctx.send('Gestoppt')

    #Abkürzung

Um Musik mit duscird.py abzuspielen, müssen Sie die AudioSource an den VoiceClient Play Collout übergeben. Ffmpeg ist erforderlich, um AudioSource zu erstellen. Wenn Sie eine ffmpeg-Umgebung haben, können Sie sehr einfach Musik abspielen, indem Sie einfach den Pfad zur Datei angeben.

Um die Wiedergabe zu stoppen, rufen Sie das Collout vc.stop auf.

Führen Sie eine kontinuierliche Wiedergabe durch

In der aktuellen Implementierung wechselt die Musik, sobald Sie "$ play" aufrufen. Ändern Sie dies in eine Spezifikation, die der Warteschlange Musik hinzufügt, wenn "$ play" ausgeführt wird, und die nächste Musik abspielt, wenn die vorherige Musik beendet ist (sogenannte Wiedergabeliste).

Verwenden Sie "asyncio.Queue" und "asyncio.Event", um die Wiedergabeliste zu implementieren. Sie werden häufig für Implementierungen verwendet, die warten, bis ein Element zur Warteschlange hinzugefügt wird, und für Implementierungen, die warten, bis ein Flag an einer anderen Stelle gesetzt wird.

python:./src/app/dbot/cogs/Voice.py


import discord
from glob import glob
import os
import asyncio
from discord.ext import commands
from typing import Dict
from dbot.core.bot import DBot


class AudioQueue(asyncio.Queue):
    def __init__(self):
        super().__init__(100)

    def __getitem__(self, idx):
        return self._queue[idx]

    def to_list(self):
        return list(self._queue)

    def reset(self):
        self._queue.clear()


class AudioStatus:
    def __init__(self, ctx: commands.Context, vc: discord.VoiceClient):
        self.vc: discord.VoiceClient = vc
        self.ctx: commands.Context = ctx
        self.queue = AudioQueue()
        self.playing = asyncio.Event()
        asyncio.create_task(self.playing_task())

    async def add_audio(self, title, path):
        await self.queue.put([title, path])

    def get_list(self):
        return self.queue.to_list()

    async def playing_task(self):
        while True:
            self.playing.clear()
            try:
                title, path = await asyncio.wait_for(self.queue.get(), timeout=180)
            except asyncio.TimeoutError:
                asyncio.create_task(self.leave())
            src = discord.FFmpegPCMAudio(path)
            self.vc.play(src, after=self.play_next)
            await self.ctx.send(f'{title}Spielen...')
            await self.playing.wait()

    def play_next(self, err=None):
        self.playing.set()

    async def leave(self):
        self.queue.reset()
        if self.vc:
            await self.vc.disconnect()
            self.vc = None

    @property
    def is_playing(self):
        return self.vc.is_playing()

    def stop(self):
        self.vc.stop()


class Voice(commands.Cog):
    def __init__(self, bot: DBot):
        self.bot = bot
        self.audio_statuses: Dict[int, AudioStatus] = {}

    @commands.command()
    async def join(self, ctx: commands.Context):
        #Nicht am Sprachkanal teilnehmen
        if not ctx.author.voice or not ctx.author.voice.channel:
            return await ctx.send('Treten Sie zuerst dem Sprachkanal bei')
        vc = await ctx.author.voice.channel.connect()
        self.audio_statuses[ctx.guild.id] = AudioStatus(ctx, vc)

    @commands.command()
    async def play(self, ctx: commands.Context, *, title: str = ''):
        status = self.audio_statuses.get(ctx.guild.id)
        if status is None:
            await ctx.invoke(self.join)
            status = self.audio_statuses[ctx.guild.id]
        music_pathes = glob('./music/**.mp3')
        music_titles = [
            os.path.basename(path).rstrip('.mp3')
            for path in music_pathes
        ]
        if not title in music_titles:
            return await ctx.send('Es gibt kein bestimmtes Lied.')
        idx = music_titles.index(title)
        await status.add_audio(title, music_pathes[idx])
        await ctx.send(f'{title}Wurde zur Wiedergabeliste hinzugefügt')

    @commands.command()
    async def stop(self, ctx: commands.Context):
        status = self.audio_statuses.get(ctx.guild.id)
        if status is None:
            return await ctx.send('Bot ist dem Sprachkanal noch nicht beigetreten')
        if not status.is_playing:
            return await ctx.send('Schon gestoppt')
        await status.stop()
        await ctx.send('Gestoppt')

    @commands.command()
    async def leave(self, ctx: commands.Context):
        status = self.audio_statuses.get(ctx.guild.id)
        if status is None:
            return await ctx.send('Ich bin dem Sprachkanal noch nicht beigetreten')
        await status.leave()
        del self.audio_statuses[ctx.guild.id]

    @commands.command()
    async def queue(self, ctx: commands.Context):
        status = self.audio_statuses.get(ctx.guild.id)
        if status is None:
            return await ctx.send('Treten Sie zuerst dem Sprachkanal bei')
        queue = status.get_list()
        songs = ""
        for i, (title, _) in enumerate(queue):
            songs += f"{i+1}. {title}\n"
        await ctx.send(songs)


def setup(bot):
    return bot.add_cog(Voice(bot))

Erstellen Sie eine neue Klasse mit dem Namen "AudioStatus", um VoiceClient- und Serverinformationen zusammen zu registrieren und die Instanz zu speichern.

AudioStatus ruft bei der Initialisierung eine Funktion namens asyncio.create_task auf. Wie der Name schon sagt, wird die Aufgabe des Musikspielens aus dem Collout erstellt. Auf diese Weise ist ein asynchroner Zugriff möglich, z. B. das Antworten auf Befehle von anderen Servern, während andere Aufgaben auf der Aufgabenseite ausgeführt werden. Die Eigenschaft play ist asyncio.Event, die verwendet wird, wenn Sie ein Flag setzen möchten, wenn eine bestimmte Bedingung erfüllt ist, und warten, ohne etwas zu tun, bis das Flag gesetzt ist. play_task ruft clear und wait auf und play_next ruft set auf, aber "setze das Flag auf False "," warte bis das Flag True wird "und" Es ist ein Prozess, bei dem "das Flag auf" True "gesetzt wird". play_next wird an das after-Argument von vc.play übergeben, das dem Argument gibt, was Sie tun möchten, wenn die Wiedergabe eines Songs beendet ist. Durch Setzen des Flags für die Wiedergabe des nächsten Songs am Ende des Songs wird die Schleife "play_task" erneut gestartet.

Die Eigenschaft "queue" dieses "AudioStatus" erbt "asyncio.Queue" und erleichtert das Abrufen der Songliste. Diese "asyncio.Queue" hat jedoch keinen Wert, der beim Aufrufen von "get" zurückgegeben werden kann. Wartet dort darauf, dass neue Elemente im laufenden Betrieb hinzugefügt werden. Dadurch ist es möglich, eine Wartezeit für die Song-Eingabe festzulegen. Und wenn 3 Minuten lang keine Eingabe erfolgt, wird das Kollout "Verlassen" aufgerufen und der Sprachkanal automatisch verlassen.

Image from Gyazo

Dies ermöglicht das Hinzufügen einer Musikwiedergabefunktion mit einem Song-Cue.

Am Ende

Die Musikwiedergabefunktion ist jetzt implementiert! Ich bin froh, dass es einfach war! Die Sprachaufnahmefunktion ist genauso einfach, nicht wahr? ?? ??

Es scheint, dass dies nicht gesagt werden kann. Als Vorbereitung für die Implementierung der Aufnahmefunktion werden wir uns genauer ansehen, wie die API-Funktion von Discord gehandhabt wird, indem wir sie direkt kontaktieren und implementieren, ohne discord.py zu verwenden. ..

Recommended Posts

Discord Bot mit Aufnahmefunktion ab Python: (4) Musikdateien abspielen
Discord Bot mit Aufnahmefunktion beginnend mit Python: (3) Zusammenarbeit mit der Datenbank
Discord Bot mit Aufnahmefunktion beginnend mit Python: (1) Einführung discord.py
Spielen Sie eine Audiodatei von Python mit Interrupt ab
Discord Bot Himbeere Pi Null mit Python [Hinweis]
GRPC beginnend mit Python
So bedienen Sie die Discord API mit Python (Bot-Registrierung)
Erstellen Sie einen Mastodon-Bot mit einer Funktion, die automatisch mit Python antwortet
Python-Anfänger startet Discord Bot
Verbessertes Lernen ab Python
Sortieren von Bilddateien mit Python (2)
Sortieren Sie große Dateien mit Python
Sortieren von Bilddateien mit Python (3)
[Python] Spielen Sie mit Discords Webhook.
Bilddateien mit Python sortieren
Integrieren Sie PDF-Dateien in Python
TXT-Dateien mit Python lesen
Python beginnend mit Hallo Welt!
Extrahieren Sie Zip-Dateien rekursiv mit Python
[Python] POST-WAV-Dateien mit Anforderungen [POST]
Mit OpenSSL mit Python 3 verschlüsselte Dateien entschlüsseln
Lass uns mit Python mit Python spielen [Anfänger]
Behandeln Sie Excel-CSV-Dateien mit Python
Lesen Sie Dateien parallel zu Python
Datenanalyse beginnend mit Python (Datenvisualisierung 1)
Datenanalyse beginnend mit Python (Datenvisualisierung 2)
Systemhandel ab Python3: langfristige Investition
Videowiedergabe mit Ton auf Python !! (tkinter / imageio)
[AWS] Verwenden von INI-Dateien mit Lambda [Python]
Erstellen Sie mit Class einen Python-Funktionsdekorator
Spielen Sie handschriftliche Zahlen mit Python Teil 2 (identifizieren)
"Systemhandel beginnt mit Python3" Lesememo
Fraktal zum Erstellen und Spielen mit Python
Geschäftseffizienz von Grund auf mit Python
Starten Sie den Discord Python-Bot 24 Stunden lang.
Mit openssl verschlüsselte Dateien werden mit openssl aus Python entschlüsselt
Ich möchte mit aws mit Python spielen
Laden Sie mit Python Dateien im Web herunter
Python lernen! Vergleich mit Java (Grundfunktion)
[Easy Python] Lesen von Excel-Dateien mit openpyxl
Datenanalyse beginnend mit Python (Datenvorverarbeitung - maschinelles Lernen)
"Erste elastische Suche" beginnend mit einem Python-Client
[Easy Python] Lesen von Excel-Dateien mit Pandas
Spielen Sie Audiodateien mit Interrupts mit PyAudio ab
Machen wir einen Twitter-Bot mit Python!
Wenn Sie einen Discord-Bot mit Python erstellen möchten, verwenden wir ein Framework