78 lines
2.2 KiB
JavaScript
78 lines
2.2 KiB
JavaScript
const { SlashCommandBuilder } = require('discord.js');
|
|
const {
|
|
joinVoiceChannel,
|
|
createAudioPlayer,
|
|
createAudioResource,
|
|
StreamType,
|
|
} = require('@discordjs/voice');
|
|
const { spawn } = require('node:child_process');
|
|
|
|
module.exports = {
|
|
data: new SlashCommandBuilder()
|
|
.setName('play')
|
|
.setDescription('Proof-of-life music command'),
|
|
|
|
async execute(interaction) {
|
|
const voiceChannel = interaction.member.voice.channel;
|
|
if (!voiceChannel) {
|
|
return interaction.reply({
|
|
content: '❌ You need to join a voice channel first!',
|
|
ephemeral: true,
|
|
});
|
|
}
|
|
|
|
// 1️⃣ Defer reply immediately to prevent 3s timeout
|
|
await interaction.deferReply();
|
|
|
|
try {
|
|
// 2️⃣ Spawn yt-dlp → ffmpeg → Discord
|
|
const ytdlp = spawn('yt-dlp', [
|
|
'-f', 'bestaudio',
|
|
'-o', '-',
|
|
'https://www.youtube.com/watch?v=rPwUh0gqv7Y', // test URL
|
|
]);
|
|
|
|
const ffmpeg = spawn('ffmpeg', [
|
|
'-i', 'pipe:0',
|
|
'-f', 's16le', // raw PCM for Discord
|
|
'-ar', '48000', // sample rate
|
|
'-ac', '2', // stereo
|
|
'pipe:1',
|
|
]);
|
|
|
|
ytdlp.stdout.pipe(ffmpeg.stdin);
|
|
|
|
const resource = createAudioResource(ffmpeg.stdout, {
|
|
inputType: StreamType.Raw,
|
|
});
|
|
|
|
const player = createAudioPlayer();
|
|
player.play(resource);
|
|
|
|
const connection = joinVoiceChannel({
|
|
channelId: voiceChannel.id,
|
|
guildId: interaction.guild.id,
|
|
adapterCreator: interaction.guild.voiceAdapterCreator,
|
|
});
|
|
|
|
connection.subscribe(player);
|
|
|
|
// 3️⃣ Edit deferred reply
|
|
await interaction.editReply('▶️ Now playing: Rick Astley!');
|
|
|
|
// Optional logging
|
|
ytdlp.stderr.on('data', (data) => console.error('yt-dlp:', data.toString()));
|
|
ffmpeg.stderr.on('data', (data) => console.error('ffmpeg:', data.toString()));
|
|
player.on('error', (err) => console.error('AudioPlayer error:', err));
|
|
} catch (err) {
|
|
console.error(err);
|
|
|
|
// 4️⃣ Safe error reply
|
|
if (interaction.deferred || interaction.replied) {
|
|
await interaction.editReply('❌ Failed to play audio!');
|
|
} else {
|
|
await interaction.reply('❌ Failed to play audio!');
|
|
}
|
|
}
|
|
},
|
|
}; |