【Node.js】discord.jsでplay-dlを使って音楽を流したい!(有識者さん助けて…)

2025年4月8日

はじめに

もともとytdl-coreを使って音楽Botなるものを作成してみたりしてあそんでいました。しかしytdl-coreでうまく動かなくなり(Youtubeの仕様変更等?)play-dlのほうがいいよ?というchatGPTさまの甘言にそそのかされ、ライブラリを変更してやってみました。しかし、どう頑張ってもエラーは出ないのに、音楽が再生されない。

player.state.statusを確認してみるとずっとbufferingの状態で再生できていないことが分かった。bufferingになっている要因として色々調べた結果createAudioResourceで取得したresourceの中身が_readableState.buffer.length が 0 で、flowing も false。Encoder の _buffer に<Buffer >という空のバッファがある。などいろいろ原因があるっぽいが修正方法がわからず。なんなら調べてるとplay-dlはしんだとかいう話もみた

疲れたので一旦一休みします。わかる有識者の方いたらぜひご教授ください…

コード

const Discord = require("discord.js");
const { EmbedBuilder } = require('discord.js');
const config = require("../config.js");
const functions = require("../functions.js");
const request = require("request");
const play = require("play-dl");
const { entersState, AudioPlayerStatus, NoSubscriberBehavior, getVoiceConnection,createAudioPlayer, createAudioResource, joinVoiceChannel, StreamType } = require('@discordjs/voice');
const { setTimeout } = require('node:timers/promises');
play.setToken({
  youtube: {
    cookie: "{クッキーを入れる}"
  }
});

const queue = [];
let connection;
let player = createAudioPlayer({
  behaviors: {
    noSubscriber: NoSubscriberBehavior.Play
  }
});

module.exports = {
  name: "messageCreate",
  async execute(message, client) {
    if (!message.guild) return;
    const channel = message.member.voice.channel;

    if (message.content.match(/vc/)) {
      if (message.author.bot) return;
      connection = joinVoiceChannel({
        channelId: channel.id,
        guildId: message.guild.id,
        adapterCreator: message.guild.voiceAdapterCreator
      });
      connection.subscribe(player);
      connection.on("stateChange", (oldState, newState) => {
        console.log(`🔄 Connection state changed: ${oldState.status} -> ${newState.status}`);
      });
      return message.reply(`接続しました`)
    }

    if (message.content.match(/https:/)) {
      const url = message.content;
      
      if (!await play.validate(url)) {
        return message.reply("このURLは処理できません。");
      }
  
      queue.push(url);
      console.log(queue);
  
      if (!connection) {
        const channel = message.member.voice.channel;
        if (!channel) return message.reply("ボイスチャンネルに参加してください!");
        const connection = getVoiceConnection(message.channel.guild.id);
        connection.subscribe(player);
        connection.on("stateChange", (oldState, newState) => {
          console.log(`🔄 Connection state changed: ${oldState.status} -> ${newState.status}`);
        });
      }
  
      if (queue.length === 1) {
        playNext(message);
      }
    }
    if (message.content.match(/bye/)) {
      if (message.author.bot) return;
      // queue = [];
      const connection = getVoiceConnection(message.channel.guild.id);
      connection.destroy();
      return message.reply(`切断しました`)
    }
  }
}
  
  async function playNext(message) {
    if (queue.length === 0) return;
    
    try {
      console.log("再生開始: ", queue[0]);
      if (!await play.validate(queue[0])) {
        return message.reply("このURLは処理できません。");
      }
      let stream = await play.stream(queue[0], {discordPlayerCompatibility:true});
      // let stream = await play.stream(queue[0]);
      // console.log("🎥 Stream情報:", stream);
      // console.log("Stream type:", stream.type);
      // let video_info = await play.video_basic_info(queue[0]);
      // console.log("🎥 Video情報:", video_info);
  
      const resource = createAudioResource(stream.stream, {
        // inputType: stream.type
        // inputType: stream.type === StreamType.OggOpus ? StreamType.OggOpus : StreamType.Arbitrary
        // inputType: StreamType.Opus
        inputType: StreamType.Arbitrary,
        // inputType: StreamType.OggOpus
        // inputType: StreamType.WebmOpus,

        inlineVolume: true
      });
      // console.log("🎼 AudioResource を作成しました:", resource);
      console.log("🔍 stream.flowing:", stream.stream.readableFlowing);
      // stream.stream.resume();
      // console.log("🎵 ストリームを手動で再開しました!");
      stream.stream.once("data", (chunk) => {
        console.log("🎶 ストリームデータを受信:", chunk);
      });
      resource.playStream.on("readable", () => {
        console.log("🎶 ストリームが読み込まれました!");
      });

      player.play(resource);
      console.log("🎼 AudioResource を作成しました:", resource);
      console.log("▶️ player.play(resource) を実行しました。");
      console.log("📢 player 状態:", player.state.status);
      await setTimeout(10000);
      console.log("📢 player 状態:", player.state.status);

      player.on(AudioPlayerStatus.Playing, () => {
        console.log("▶️ 再生開始!");
      });
      
      player.on(AudioPlayerStatus.Buffering, () => {
        console.log("⏳ バッファリング中...");
      });
      
      player.on(AudioPlayerStatus.Idle, () => {
        console.log("⏹️ 再生終了、次の曲を再生");
        queue.shift();
        playNext(message);
      });
      
      player.on("error", (error) => {
        console.error("❌ 再生エラー:", error);
      });
  
      await new Promise((resolve) => {
        player.once(AudioPlayerStatus.Idle, resolve);
      });
  
    } catch (error) {
      console.error("エラー: ", error);
      message.channel.send("エラーが発生しました。次の曲にスキップします。");
      queue.shift();
      playNext(message);
    }
  }

buffering状態でさらに放置すると以下のエラーが出ることにも気づいた

[2025/2/22 14:34:50] Error: While getting info from url
Video unavailable