Music not playing in Discord voice channel

763 Views Asked by At

I'm trying to add the functionality of playing music to my custom Discord bot. I'm writing it in Java 11, Spring Boot, Discord4j and the LavaPlayer library.

I have the command !join that makes the Discord bot join the voice channel of the person who typed the command. This works but I assign the LavaPlayerAudioProvider to the VoiceState once the bot joins. So the mistake might occur here (I don't know). This is the class for the !join command:

@RequiredArgsConstructor
public class JoinCommandHandler implements CommandStrategy {
    private final LavaPlayerAudioProvider provider;

    /**
     * {@inheritDoc}
     * <p>
     * Makes the bot join the voice channel of the user.
     */
    @Override
    public Mono<Void> execute(Message message) {
        return Mono.just(message)
                .flatMap(Message::getAuthorAsMember)
                .flatMap(m -> m.getVoiceState()
                        .flatMap(VoiceState::getChannel))
                .flatMap(c -> c.join(spec -> spec.setProvider(provider)))
                .then();
    }

    @Override
    public String getCommandName() {
        return "!join";
    }

    @Override
    public String getDescription() {
        return "**Under Development** Make this bot join your channel.";
    }
}

To be honest, I don't think the mistake is going to be in the !join command but it might be usefull to understand the question more.

I have the audio configurations set in the AudioConfig class like this:

@Configuration
public class AudioConfig {
    @Bean
    public AudioPlayerManager audioPlayerManager() {
        final AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
        playerManager.getConfiguration().setFrameBufferFactory(NonAllocatingAudioFrameBuffer::new);
        AudioSourceManagers.registerLocalSource(playerManager);
        return playerManager;
    }

    @Bean
    public AudioPlayer audioPlayer() {
        return audioPlayerManager().createPlayer();
    }

    @Bean
    public AudioProvider audioProvider() {
        return new LavaPlayerAudioProvider(audioPlayer());
    }
}

I wrote a !play [youtube url] command to play music once the bot joined a voice channel.

@Service("!play")
@RequiredArgsConstructor
public class PlayCommand implements CommandStrategy {
    private final AudioPlayerManager manager;
    private final TrackScheduler trackScheduler;

    /**
     * {@inheritDoc}
     * <p>
     * Makes the bot queue a song and play in the channel.
     */
    @Override
    public Mono<Void> execute(Message message) {
        return Mono.just(message.getContent())
                .map(content -> Arrays.asList(content.split(" ")))
                .doOnNext(command -> manager.loadItem(command.get(1), trackScheduler)).then();
    }
}

And here the LavaPlayerAudioProviver to provide the music:

@Component
public final class LavaPlayerAudioProvider extends AudioProvider {
    private final AudioPlayer audioPlayer;
    private final MutableAudioFrame frame = new MutableAudioFrame();

    public LavaPlayerAudioProvider(AudioPlayer audioPlayer) {
        super(ByteBuffer.allocate(StandardAudioDataFormats.DISCORD_OPUS.maximumChunkSize()));
        this.frame.setBuffer(getBuffer());
        this.audioPlayer = audioPlayer;
    }

    @Override
    public boolean provide() {
        final boolean didProvide = audioPlayer.provide(frame);
        if (didProvide) {
            getBuffer().flip();
        }
        return didProvide;
    }
}

Now here comes the tricky part. I debugged the code to see step by step how the code executes after I type the !play [url] command.

When the code reaches the TrackScheduler class, it doesn't execute the trackLoaded() method to play the song, instead it executes the noMatches() method, which basically means the URL can not be found and thus no music can be played. You can find the TrackScheduler class here:

@Component
@RequiredArgsConstructor
public class TrackScheduler implements AudioLoadResultHandler {
    private final AudioPlayer player;

    @Override
    public void trackLoaded(final AudioTrack track) {
        // LavaPlayer found an audio source for us to play
        player.playTrack(track);
    }

    @Override
    public void playlistLoaded(final AudioPlaylist playlist) {
        // LavaPlayer found multiple AudioTracks from some playlist
    }

    @Override
    public void noMatches() {
        // LavaPlayer did not find any audio to extract
    }

    @Override
    public void loadFailed(final FriendlyException exception) {
        // LavaPlayer could not parse an audio source for some reason
    }
}

I tried different URLs but they all end up going in the noMatch() method... I followed the Discord4J guide (https://docs.discord4j.com/music-bot-tutorial) but they're not using Spring Boot so it's a little puzzle..

If you need more info, I'll be glad to give it to you. Thanks in advance guys!

1

There are 1 best solutions below

0
Karafra On

Okay, I know its a bit late but here you go.

Your problem is, that you are registering only local sources for audioplayer. That means, that lavaplayer does not know what are sites like Facebook, Youtube or Vimeo and how to process them.

You should add AudioSourceManagers.registerRemoteSources(audioPlayerManager); to your configuration class.

So final configuration class will look like this:

@Configuration
public class AudioConfig {
    @Bean
    public AudioPlayerManager audioPlayerManager() {
        final AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
        playerManager.getConfiguration().setFrameBufferFactory(NonAllocatingAudioFrameBuffer::new);
        AudioSourceManagers.registerLocalSource(playerManager);
        AudioSourceManagers.registerRemoteSources(audioPlayerManager);
        return playerManager;
    }

    @Bean
    public AudioPlayer audioPlayer() {
        return audioPlayerManager().createPlayer();
    }

    @Bean
    public AudioProvider audioProvider() {
        return new LavaPlayerAudioProvider(audioPlayer());
    }
}

Other that that, your code works fine for me. And I would suggest removing AudioSourceManagers.registerLocalSource(playerManager); altogether, I dont see any reason why expose server side files this way.