diff --git a/src/client/voice/util/PlayInterface.js b/src/client/voice/util/PlayInterface.js index 07130c31b..f814683d3 100644 --- a/src/client/voice/util/PlayInterface.js +++ b/src/client/voice/util/PlayInterface.js @@ -1,7 +1,10 @@ +const { Readable } = require('stream'); +const prism = require('prism-media'); + /** * Options that can be passed to stream-playing methods: * @typedef {Object} StreamOptions - * @property {string} [type='unknown'] The type of stream. 'unknown', 'converted', 'opus', 'broadcast. + * @property {StreamType} [type='unknown'] The type of stream. 'unknown', 'converted', 'opus', 'broadcast. * @property {number} [seek=0] The time to seek to * @property {number|boolean} [volume=1] The volume to play at. Set this to false to disable volume transforms for * this stream to improve performance. @@ -15,6 +18,17 @@ * 1 means that changes in volume will be more instant. */ +/** + * An option passed as part of `StreamOptions` specifying the type of the stream. + * * `unknown`: The default type, streams/input will be passed through to ffmpeg before encoding. + * Will play most streams. + * * `converted`: Play a stream of 16bit signed stereo PCM data, skipping ffmpeg. + * * `opus`: Play a stream of opus packets, skipping ffmpeg. You lose the ability to alter volume. + * * `ogg/opus`: Play an ogg file with the opus encoding, skipping ffmpeg. You lose the ability to alter volume. + * * `webm/opus`: Play a webm file with opus audio, skipping ffmpeg. You lose the ability to alter volume. + * @typedef {string} StreamType + */ + /** * An interface class to allow you to play audio over VoiceConnections and VoiceBroadcasts. */ @@ -25,30 +39,42 @@ class PlayInterface { /** * Play an audio resource. - * @param {ReadableStream|string} resource The resource to play. + * @param {VoiceBroadcast|ReadableStream|string} resource The resource to play. * @param {StreamOptions} [options] The options to play. * @example * // Play a local audio file * connection.play('/home/hydrabolt/audio.mp3', { volume: 0.5 }); - * + * @example * // Play a ReadableStream * connection.play(ytdl('https://www.youtube.com/watch?v=ZlAU_w7-Xp8', { filter: 'audioonly' })); - * + * @example + * // Play a voice broadcast + * const broadcast = client.createVoiceBroadcast(); + * broadcast.play('/home/hydrabolt/audio.mp3'); + * connection.play(broadcast); + * @example * // Using different protocols: https://ffmpeg.org/ffmpeg-protocols.html * connection.play('http://www.sample-videos.com/audio/mp3/wave.mp3'); * @returns {StreamDispatcher} */ play(resource, options = {}) { - const type = resource instanceof Broadcast ? options.type || 'unknown' : 'broadcast'; + if (resource instanceof Broadcast) { + if (!this.player.playBroadcast) throw Error('VOICE_PLAY_INTERFACE_NO_BROADCAST'); + return this.player.playBroadcast(resource, options); + } + const type = options.type || 'unknown'; if (type === 'unknown') { return this.player.playUnknown(resource, options); } else if (type === 'converted') { return this.player.playPCMStream(resource, options); } else if (type === 'opus') { return this.player.playOpusStream(resource, options); - } else if (type === 'broadcast') { - if (!this.player.playBroadcast) throw Error('VOICE_PLAY_INTERFACE_NO_BROADCAST'); - return this.player.playBroadcast(resource, options); + } else if (type === 'ogg/opus') { + if (!(resource instanceof Readable)) throw Error('VOICE_PRISM_DEMUXERS_NEED_STREAM'); + return this.player.playOpusStream(resource.pipe(new prism.OggOpusDemuxer())); + } else if (type === 'webm/opus') { + if (!(resource instanceof Readable)) throw Error('VOICE_PRISM_DEMUXERS_NEED_STREAM'); + return this.player.playOpusStream(resource.pipe(new prism.WebmOpusDemuxer())); } throw Error('VOICE_PLAY_INTERFACE_BAD_TYPE'); } diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 91956cf99..a57ef6eed 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -56,6 +56,7 @@ const Messages = { VOICE_JOIN_SOCKET_CLOSED: 'Tried to send join packet, but the WebSocket is not open.', VOICE_PLAY_INTERFACE_NO_BROADCAST: 'A broadcast cannot be played in this context.', VOICE_PLAY_INTERFACE_BAD_TYPE: 'Unknown stream type', + VOICE_PRISM_DEMUXERS_NEED_STREAM: 'To play a webm/ogg stream, you need to pass a ReadableStream.', OPUS_ENGINE_MISSING: 'Couldn\'t find an Opus engine.',