Discord BOT音乐播放器:一次播放3首歌曲

时间:2020-08-15 17:30:00

标签: ffmpeg queue discord audio-player dsharp+

我正在开发播放音乐的Discord机器人,我正在使用DSahrpPlus作为包装器,并使该机器人可以正常工作

命令得到核心解析

using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.CommandsNext.Attributes;
using DSharpPlus.Entities;
using DSharpPlus.VoiceNext;

using WolfBot.Attributes;
using WolfBot.Commands.Music;

namespace WolfBot.Commands
{
    class MusicCommands : BaseCommandModule
    {
        int SongID = 1;
        MusicPlayer player;
        [Command("join")]
        [RequirePermissionsCustom(Permissions.UseVoice)]
        public async Task Join(CommandContext ctx)
        {
            //Initialize music player
            player = new MusicPlayer(ctx);

            var vnext = ctx.Client.GetVoiceNext();

            var vnc = vnext.GetConnection(ctx.Guild);
            if (vnc != null)
            {
                await ctx.RespondAsync("Already connected in this guild.");
                throw new InvalidOperationException("Already connected in this guild.");
            }

            var chn = ctx.Member?.VoiceState?.Channel;
            if (chn == null)
            {
                await ctx.RespondAsync("You need to be in a voice channel.");
                throw new InvalidOperationException("You need to be in a voice channel.");
            }

            vnc = await vnext.ConnectAsync(chn);
            await ctx.RespondAsync(DiscordEmoji.FromName(ctx.Client, ":ok_hand:")); //?
        }

        [Command("leave")]
        public async Task Leave(CommandContext ctx)
        {
            var vnext = ctx.Client.GetVoiceNext();

            var vnc = vnext.GetConnection(ctx.Guild);
            if (vnc == null)
                await ctx.RespondAsync("Not connected in this guild.");

            vnc.Disconnect();
            await ctx.RespondAsync(DiscordEmoji.FromName(ctx.Client, ":ok_hand:")); //?
        }

        [Command("play"), Description("Plays an audio file.")]
        public async Task Play(CommandContext ctx, [RemainingText, Description("Full path to the file to play.")] string filename)
        {
            // check whether VNext is enabled
            var vnext = ctx.Client.GetVoiceNext();
            if (vnext == null)
            {
                // not enabled
                await ctx.RespondAsync("VNext is not enabled or configured.");
                return;
            }

            // check whether we aren't already connected
            var vnc = vnext.GetConnection(ctx.Guild);
            if (vnc == null)
            {
                // already connected
                await ctx.RespondAsync("Not connected in this guild.");
                return;
            }
            Song song = new Song(filename);
            player.AddMusic(song);
            await player.Play();
        }
        [Command("list-songs"), Description("List all the songs in queue")]
        public async Task ListSongs(CommandContext ctx)
        {
            await ctx.RespondAsync(player.listSongs());
        }
        [Command("skip"), Description("Skips a song that is curently in queue")]
        public async Task Skip(CommandContext ctx)
        {
            player.Skip();
        }
    }
}

这是我的musicPlayer类

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;

using DSharpPlus.CommandsNext;
using DSharpPlus.Entities;
using DSharpPlus.VoiceNext; //for music

using WolfBot.Tools; //some extension methods are in here like GetUsername
using WolfBot.Tools.NativeTools; //To check if ffmpeg is alredy running

namespace WolfBot.Commands.Music
{
    public class MusicPlayer
    {
        CommandContext ctx; //Used for sending commands to discord
        public Queue<Song> qs;
        VoiceTransmitStream transmitStream; //Discord Audio buffer
        Song playingSong;

        string filename;

        bool isSongPlaying = false; //Does ffmpeg play a song?

        //FFMpeg proces (our audio player)
        ProcessStartInfo ffmpeg_inf;
        Process ffmpeg;
        void ConfigureFFMpeg()
        {
            ffmpeg_inf = new ProcessStartInfo();
            ffmpeg_inf.FileName = "ffmpeg";
            
            ffmpeg_inf.UseShellExecute = false;
            ffmpeg_inf.RedirectStandardOutput = true;
            ffmpeg_inf.RedirectStandardError = true;
        }
        public MusicPlayer(CommandContext ctx)
        {
            this.ctx = ctx;
            qs = new Queue<Song>();

            //Configure ffmpeg for playing audio
            ConfigureFFMpeg();
        }
        public void AddMusic(Song s)
        {
            qs.Enqueue(s); //Add one song to the queue
        }
        public void RemoveMusic(Song s)
        {
            qs.Dequeue();
        }
        public void AddMusic(Queue<Song> qs)
        {
            this.qs = qs; //Load a list of songs
        }


        public async Task Play()
        {
            // check whether VNext is enabled
            var vnext = ctx.Client.GetVoiceNext();
            if (vnext == null)
            {
                // not enabled
                await ctx.RespondAsync("VNext is not enabled or configured.");
                return;
            }

            // check whether we aren't already connected
            var vnc = vnext.GetConnection(ctx.Guild);
            if (vnc == null)
            {
                // already connected
                await ctx.RespondAsync("Not connected in this guild.");
                return;
            }

            // wait for current playback to finish
            while (vnc.IsPlaying)
            {
                await ctx.RespondAsync($"Added to Queue: `{qs.Peek().Name}` | Requested by: {UserExtension.GetUsername(ctx.Message.Author)}");
                await vnc.WaitForPlaybackFinishAsync();
            }

            // play
            Exception exc = null;
            await vnc.SendSpeakingAsync(true);
            try
            {
                filename = qs.Peek().URL; //Set song filename

                transmitStream = vnc.GetTransmitStream(); //Get Voice transmission stream

                // check if music input is url and if it is not check if file exists
                if (!WolfBot.Tools.Network.StringNetworkTools.IsURL(filename))
                {
                    if (!File.Exists(filename))
                    {
                        // file does not exist
                        await ctx.RespondAsync($"File `{filename}` does not exist.");
                        //Remove the invalid file from the queue
                        qs.Dequeue();
                    }
                }
                else
                {
                    //If the song is not skipped play it
                    if (!qs.Peek().isSkipped)
                    {
                        await ctx.Message.RespondAsync($"Now Playing `{qs.Peek().Name}` | Requested by: {UserExtension.GetUsername(ctx.Message.Author)}");
                        playingSong = qs.Peek(); //Add playing song

                        //Play the damm song :)
                        PlayInternal(filename);
                    }
                    else
                    {
                        await ctx.Message.RespondAsync($"Song: `{qs.Peek().Name}` | Requested by: {UserExtension.GetUsername(ctx.Message.Author)} is skipped");
                    }
                }

            }
            catch (Exception ex) { exc = ex; }
            finally
            {
                await vnc.SendSpeakingAsync(false);
            }

            if (exc != null)
                await ctx.RespondAsync($"An exception occured during playback: `{exc.GetType()}: {exc.Message}`");
        }
        
        public void Pause()
        {
            //Not yet sure how to implement this
        }
        public void Stop()
        {
            //Not yet sure how to implement this
        }
        public void Next()
        {
            //Not yet sure how to implement this
        }
        public void Skip()
        {
            qs.Peek().isSkipped = true;
        }
        /// <summary>
        /// Lists songs you are having on queue
        /// </summary>
        /// <returns></returns>
        public string listSongs()
        {
            List<string> songs = new List<string>();
            foreach (Song s in qs)
                songs.Add(s.Name);
            if (qs.Count > 0)
                return string.Join('\n', songs);
            else
                return "There are no songs";
        }
        /// <summary>
        /// Invokes FFMPeg with filename to play
        /// </summary>
        /// <returnes>
        /// Task.Finsiehd when song finishes playing
        /// </returnes>
        void PlayInternal(string filename)
        {
            ffmpeg_inf.Arguments = $"-i \"{filename}\" -ac 2 -f s16le -ar 48000 pipe:1";
            try
            {
                //Only start ffmpeg if its not already started
                if (!ProcessHelpers.IsRunning("ffmpeg.exe"))
                    {
                    ffmpeg = Process.Start(ffmpeg_inf);
                    this.isSongPlaying = true;
                    }


                ffmpeg.EnableRaisingEvents = true;
                ffmpeg.Exited += new EventHandler(ffmpeg_finished);

                var ffout = ffmpeg.StandardOutput.BaseStream;
                ffout.CopyTo(transmitStream); //Copy audio to the Discord buffer
            }
            catch (Exception ex)
            {
                Console.WriteLine("Something bad happened :(: " + ex.Message);
                return;
            }
        }
        void ffmpeg_finished(object sender, EventArgs e)
        {
            this.isSongPlaying = false;
            qs.Dequeue(); //Remove last played element from queue
        }
    }
}

歌曲类不仅仅是一些属性

public string Name { get; private set; }
public string URL { get; private set; }
public bool isSkipped { get; set; } = false; //also used when song finished playing

public Song(string file)
{
    if (StringNetworkTools.IsURL(file))
    {
        string href = StringNetworkTools.FileFromURL(file);
        this.Name = href.Split('.')[0];
        this.URL = file;
    }
    else
    {
        this.Name = file.Split('.')[0];
        this.URL = file;
    }
}

一切正常,直到您开始通过在上一首歌曲没有完成播放的同时将歌曲添加到队列中来阻塞播放按钮时,尽管机器人会通知您一旦歌曲停止播放,它将被添加到队列中上一首歌曲然后立即播放所有歌曲,而不是按照队列播放,所以我不知道自己在做什么错?

如果有人有任何想法,请告诉我

感谢大家的问候和问候

歌曲

0 个答案:

没有答案