serenity-rs / songbird

An async Rust library for the Discord voice API
ISC License
384 stars 110 forks source link

The play method is playing the previous input source instead of the currecnt source. #178

Closed phoxwupsh closed 1 year ago

phoxwupsh commented 1 year ago

Songbird version: 0.3.2

Rust version (rustc -V): 1.69.0

Serenity/Twilight version: 0.11.5

Output of ffmpeg -version, yt-dlp --version (if relevant):

ffmpeg -version
ffmpeg version 6.0-full_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developers
built with gcc 12.2.0 (Rev10, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24 --enable-libdav1d --enable-libdavs2 --enable-libuavs3d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libaom --enable-libjxl --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libvpl --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil      58.  2.100 / 58.  2.100
libavcodec     60.  3.100 / 60.  3.100
libavformat    60.  3.100 / 60.  3.100
libavdevice    60.  1.100 / 60.  1.100
libavfilter     9.  3.100 /  9.  3.100
libswscale      7.  1.100 /  7.  1.100
libswresample   4. 10.100 /  4. 10.100
libpostproc    57.  1.100 / 57.  1.100
yt-dlp --version
2023.03.04

Description: The problem is like, for example, I send play command and use YouTube link A as an argument, it can play A correctly, then I send play command and use another YouTube link B as argument while A is still playing, it will stop A and play A again, instead of playing B. While it is playing A in the second time (that the input is actually B), and I send play command and use B as argument again, then it stop A and start playing B. Consequently, if I need to stop the track playing currently and start playing another track by using play, I have to send the play command twice. In my code, I implement my play command myself, and I'm not using the builtin-queue feature, I implement a HashMap to store what is playing for each guild instead. Below is my code:

#[command]
async fn play(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
    let guild = msg.guild(&ctx.cache).unwrap();
    let guild_id = guild.id;

    // Check if url is provided
    match args.single::<String>() {
        // If url is provided
        Ok(url) => {

            // Check if the user is in a voice channel
            let user_voice_channel_id = match guild.voice_states.get(&msg.author.id)
            .and_then(|voice_channel| voice_channel.channel_id) {
                Some(channel_id) => channel_id,
                None => {
                    msg.reply(ctx, "You are not in a voice channel").await?;
                    return Ok(())
                }
            };

            // Get the Songbird instance
            let manager = songbird::get(ctx).await.expect("Songbird Voice client placed in at initialization.");

            let handler_lock = match manager.get(guild_id) {
                Some(handler_lock) => {
                    // If the bot is already in a voice channel

                    let current_channel_id = guild.voice_states.get(&ctx.cache.current_user_id())
                    .and_then(|voice_state| voice_state.channel_id)
                    .unwrap();

                    if current_channel_id != user_voice_channel_id {
                    // The bot is in another channel
                        msg.reply(ctx, format!("I'm currently in {}.", current_channel_id.mention())).await?;
                        return Ok(());
                    }
                    handler_lock
                },
                None => {
                    let (handler_lock, success) = manager.join(guild_id, user_voice_channel_id).await;
                    if let Ok(_) = success {
                        msg.channel_id.say(ctx, format!("Join {}", user_voice_channel_id.mention())).await?;
                    }
                    handler_lock
                }
            };

            // Validate the URL
            if !Url::parse(&url).is_ok() {
                msg.reply(ctx, "You must provide a valid YouTube URL.").await?;
                return Ok(());
            }

            let source = ytdl(&url).await?;
            let mut handler = handler_lock.lock().await;
            let track_handle = handler.play_only_source(source);
            let title = track_handle.metadata().title.clone().unwrap();
            // Update the current track
            let playing_lock = {
                let data_read = ctx.data.read().await;
                data_read.get::<Playing>().expect("Expected Playing in TypeMap").clone()
            };
            {
                let mut playing = playing_lock.write().await;
                let _ = playing.insert(guild_id, track_handle);
            }

            // Inform the user about the song being played
            msg.reply(ctx, format!("Now playing: {}", title)).await?;

        },
        Err(_) => {
            // Check if there is a paused track
            let playing_lock = {
                let data_read = ctx.data.read().await;
                data_read.get::<Playing>().expect("Expected Playing in TypeMap").clone()
            };
            {
                let playing = playing_lock.read().await;
                if let Some(current_track) = playing.get(&guild_id) {
                    match current_track.get_info().await {
                        Ok(track_state) => match track_state.playing {
                            PlayMode::Pause => {
                                let _ = current_track.play();
                                return Ok(())
                            },
                            _ => ()
                        },
                        Err(_) => ()
                    };
                } else {
                    msg.reply(ctx, "You have to provide a url.").await?;
                }
                return Ok(())
            }
        }
    };
    Ok(())
}

Steps to reproduce: You can reproduce this with this example code by using the play command in it.

aquelemiguel commented 1 year ago

Following this thread as it seems we're also having this issue on our side.

FelixMcFelix commented 1 year ago

Thanks for the report, I suspect this is coming from how youtube-dl/yt-dlc/yt-dlp behaves. Unfortunately, I don't have too much time for non-next development -- If you can investigate and fix, then feel free!

phoxwupsh commented 1 year ago

@FelixMcFelix your suspicions are right, I update my yt-dlp version to 2023.04.27.022240, which is a nightly version, and the problem is solved. Thanks a lot!