buddy

node MVC discord bot
Log | Files | Refs | README

PlayInterface.js (4246B)


      1 'use strict';
      2 
      3 const { Readable } = require('stream');
      4 const prism = require('prism-media');
      5 const { Error } = require('../../../errors');
      6 
      7 /**
      8  * Options that can be passed to stream-playing methods:
      9  * @typedef {Object} StreamOptions
     10  * @property {StreamType} [type='unknown'] The type of stream.
     11  * @property {number} [seek=0] The time to seek to, will be ignored when playing `ogg/opus` or `webm/opus` streams
     12  * @property {number|boolean} [volume=1] The volume to play at. Set this to false to disable volume transforms for
     13  * this stream to improve performance.
     14  * @property {number} [plp] Expected packet loss percentage
     15  * @property {boolean} [fec] Enabled forward error correction
     16  * @property {number|string} [bitrate=96] The bitrate (quality) of the audio in kbps.
     17  * If set to 'auto', the voice channel's bitrate will be used
     18  * @property {number} [highWaterMark=12] The maximum number of opus packets to make and store before they are
     19  * actually needed. See https://nodejs.org/en/docs/guides/backpressuring-in-streams/. Setting this value to
     20  * 1 means that changes in volume will be more instant.
     21  */
     22 
     23 /**
     24  * An option passed as part of `StreamOptions` specifying the type of the stream.
     25  * * `unknown`: The default type, streams/input will be passed through to ffmpeg before encoding.
     26  * Will play most streams.
     27  * * `converted`: Play a stream of 16bit signed stereo PCM data, skipping ffmpeg.
     28  * * `opus`: Play a stream of opus packets, skipping ffmpeg. You lose the ability to alter volume.
     29  * * `ogg/opus`: Play an ogg file with the opus encoding, skipping ffmpeg. You lose the ability to alter volume.
     30  * * `webm/opus`: Play a webm file with opus audio, skipping ffmpeg. You lose the ability to alter volume.
     31  * @typedef {string} StreamType
     32  */
     33 
     34 /**
     35  * An interface class to allow you to play audio over VoiceConnections and VoiceBroadcasts.
     36  */
     37 class PlayInterface {
     38   constructor(player) {
     39     this.player = player;
     40   }
     41 
     42   /**
     43    * Play an audio resource.
     44    * @param {VoiceBroadcast|ReadableStream|string} resource The resource to play.
     45    * @param {StreamOptions} [options] The options to play.
     46    * @example
     47    * // Play a local audio file
     48    * connection.play('/home/hydrabolt/audio.mp3', { volume: 0.5 });
     49    * @example
     50    * // Play a ReadableStream
     51    * connection.play(ytdl('https://www.youtube.com/watch?v=ZlAU_w7-Xp8', { quality: 'highestaudio' }));
     52    * @example
     53    * // Play a voice broadcast
     54    * const broadcast = client.voice.createBroadcast();
     55    * broadcast.play('/home/hydrabolt/audio.mp3');
     56    * connection.play(broadcast);
     57    * @example
     58    * // Using different protocols: https://ffmpeg.org/ffmpeg-protocols.html
     59    * connection.play('http://www.sample-videos.com/audio/mp3/wave.mp3');
     60    * @returns {StreamDispatcher}
     61    */
     62   play(resource, options = {}) {
     63     const VoiceBroadcast = require('../VoiceBroadcast');
     64     if (resource instanceof VoiceBroadcast) {
     65       if (!this.player.playBroadcast) throw new Error('VOICE_PLAY_INTERFACE_NO_BROADCAST');
     66       return this.player.playBroadcast(resource, options);
     67     }
     68     if (resource instanceof Readable || typeof resource === 'string') {
     69       const type = options.type || 'unknown';
     70       if (type === 'unknown') {
     71         return this.player.playUnknown(resource, options);
     72       } else if (type === 'converted') {
     73         return this.player.playPCMStream(resource, options);
     74       } else if (type === 'opus') {
     75         return this.player.playOpusStream(resource, options);
     76       } else if (type === 'ogg/opus') {
     77         if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
     78         return this.player.playOpusStream(resource.pipe(new prism.opus.OggDemuxer()), options);
     79       } else if (type === 'webm/opus') {
     80         if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
     81         return this.player.playOpusStream(resource.pipe(new prism.opus.WebmDemuxer()), options);
     82       }
     83     }
     84     throw new Error('VOICE_PLAY_INTERFACE_BAD_TYPE');
     85   }
     86 
     87   static applyToClass(structure) {
     88     for (const prop of ['play']) {
     89       Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(PlayInterface.prototype, prop));
     90     }
     91   }
     92 }
     93 
     94 module.exports = PlayInterface;