fix docs
This commit is contained in:
parent
e1cc416c84
commit
9d5f065582
14 changed files with 3427 additions and 106 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -9,3 +9,6 @@ lib/
|
|||
|
||||
# error logs
|
||||
yarn-error.log
|
||||
|
||||
# demo
|
||||
demo/
|
|
@ -3,3 +3,4 @@ tslint.json
|
|||
tsconfig.json
|
||||
.prettierrc
|
||||
test/
|
||||
demo/
|
2
docs/extractors/extractor.md
Normal file
2
docs/extractors/extractor.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Discord Player Extractor API
|
||||
The extractor API
|
139
docs/general/Welcome.md
Normal file
139
docs/general/Welcome.md
Normal file
|
@ -0,0 +1,139 @@
|
|||
# Discord Player
|
||||
Complete framework to facilitate music commands using **[discord.js](https://discord.js.org)**.
|
||||
|
||||
[![downloadsBadge](https://img.shields.io/npm/dt/discord-player?style=for-the-badge)](https://npmjs.com/discord-player)
|
||||
[![versionBadge](https://img.shields.io/npm/v/discord-player?style=for-the-badge)](https://npmjs.com/discord-player)
|
||||
|
||||
## Installation
|
||||
|
||||
### Install **[discord-player](https://npmjs.com/package/discord-player)**
|
||||
|
||||
```sh
|
||||
$ npm install --save discord-player
|
||||
```
|
||||
|
||||
### Install **[@discordjs/opus](https://npmjs.com/package/@discordjs/opus)**
|
||||
|
||||
```sh
|
||||
$ npm install --save @discordjs/opus
|
||||
```
|
||||
|
||||
### Install FFmpeg or Avconv
|
||||
- Official FFMPEG Website: **[https://www.ffmpeg.org/download.html](https://www.ffmpeg.org/download.html)**
|
||||
|
||||
- Node Module (FFMPEG): **[https://npmjs.com/package/ffmpeg-static](https://npmjs.com/package/ffmpeg-static)**
|
||||
|
||||
- Avconv: **[https://libav.org/download](https://libav.org/download)**
|
||||
|
||||
# Features
|
||||
- Simple & easy to use 🤘
|
||||
- Beginner friendly 😱
|
||||
- Audio filters 🎸
|
||||
- Lightweight 🛬
|
||||
- Custom extractors support 🌌
|
||||
- Lyrics 📃
|
||||
- Multiple sources support ✌
|
||||
- Play in multiple servers at the same time 🚗
|
||||
|
||||
## [Documentation](https://discord-player.js.org)
|
||||
|
||||
## Getting Started
|
||||
|
||||
Here is the code you will need to get started with discord-player. Then, you will be able to use `client.player` everywhere in your code!
|
||||
|
||||
```js
|
||||
const Discord = require("discord.js"),
|
||||
client = new Discord.Client,
|
||||
settings = {
|
||||
prefix: "!",
|
||||
token: "Your Discord Token"
|
||||
};
|
||||
|
||||
const { Player } = require("discord-player");
|
||||
|
||||
// Create a new Player (you don't need any API Key)
|
||||
const player = new Player(client);
|
||||
|
||||
// To easily access the player
|
||||
client.player = player;
|
||||
|
||||
// add the trackStart event so when a song will be played this message will be sent
|
||||
client.player.on("trackStart", (message, track) => message.channel.send(`Now playing ${track.title}...`))
|
||||
|
||||
client.once("ready", () => {
|
||||
console.log("I'm ready !");
|
||||
});
|
||||
|
||||
client.on("message", async (message) => {
|
||||
|
||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
||||
const command = args.shift().toLowerCase();
|
||||
|
||||
// !play Despacito
|
||||
// will play "Despacito" in the voice channel
|
||||
if(command === "play"){
|
||||
client.player.play(message, args[0]);
|
||||
// as we registered the event above, no need to send a success message here
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
client.login(settings.token);
|
||||
```
|
||||
|
||||
## Supported websites
|
||||
|
||||
By default, discord-player supports **YouTube**, **Spotify** and **SoundCloud** streams only.
|
||||
|
||||
### Optional dependencies
|
||||
|
||||
Discord Player provides an **Extractor API** that enables you to use your custom stream extractor with it. Some packages have been made by the community to add new features using this API.
|
||||
|
||||
#### [@discord-player/extractor](https://github.com/Snowflake107/discord-player-extractors) (optional)
|
||||
|
||||
Optional package that adds support for `vimeo`, `reverbnation`, `facebook`, `attachment links` and `lyrics`.
|
||||
You just need to install it using `npm i --save @discord-player/extractor` (discord-player will automatically detect and use it).
|
||||
|
||||
#### [@discord-player/downloader](https://github.com/DevSnowflake/discord-player-downloader) (optional)
|
||||
|
||||
`@discord-player/downloader` is an optional package that brings support for +700 websites. The documentation is available [here](https://github.com/DevSnowflake/discord-player-downloader).
|
||||
|
||||
## Examples of bots made with Discord Player
|
||||
|
||||
These bots are made by the community, they can help you build your own!
|
||||
|
||||
* [AtlantaBot](https://github.com/Androz2091/AtlantaBot) by [Androz2091](https://github.com/Androz2091)
|
||||
* [Discord-Music](https://github.com/inhydrox/discord-music) by [inhydrox](https://github.com/inhydrox)
|
||||
* [Music-bot](https://github.com/ZerioDev/Music-bot) by [ZerioDev](https://github.com/ZerioDev)
|
||||
|
||||
## FAQ
|
||||
|
||||
### How to use cookies?
|
||||
|
||||
```js
|
||||
const player = new Player(client, {
|
||||
ytdlDownloadOptions: {
|
||||
requestOptions: {
|
||||
headers: {
|
||||
cookie: "YOUR_YOUTUBE_COOKIE"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### How to use custom proxies?
|
||||
|
||||
```js
|
||||
const HttpsProxyAgent = require("https-proxy-agent");
|
||||
|
||||
// Remove "user:pass@" if you don't need to authenticate to your proxy.
|
||||
const proxy = "http://user:pass@111.111.111.111:8080";
|
||||
const agent = HttpsProxyAgent(proxy);
|
||||
|
||||
const player = new Player(client, {
|
||||
ytdlDownloadOptions: {
|
||||
requestOptions: { agent }
|
||||
}
|
||||
});
|
||||
```
|
8
docs/index.yml
Normal file
8
docs/index.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
- name: General
|
||||
files:
|
||||
- name: Welcome
|
||||
path: welcome.md
|
||||
- name: Extractors
|
||||
files:
|
||||
- name: Extractors
|
||||
path: extractor.md
|
17
jsdoc.json
Normal file
17
jsdoc.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"source": {
|
||||
"includePattern": ".+\\.ts(doc|x)?$"
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"node_modules/jsdoc-babel"
|
||||
],
|
||||
"babel": {
|
||||
"extensions": ["ts"],
|
||||
"babelrc": false,
|
||||
"presets": [
|
||||
["@babel/preset-env", { "targets": { "node": true } }],
|
||||
"@babel/preset-typescript"
|
||||
]
|
||||
}
|
||||
}
|
10
package.json
10
package.json
|
@ -11,7 +11,9 @@
|
|||
"test": "yarn build && cd test && node index.js",
|
||||
"build": "tsc",
|
||||
"format": "prettier --write \"src/**/*.ts\"",
|
||||
"lint": "tslint -p tsconfig.json"
|
||||
"lint": "tslint -p tsconfig.json",
|
||||
"docs": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml --output demo/docs.json",
|
||||
"docs:test": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml"
|
||||
},
|
||||
"funding": "https://github.com/Androz2091/discord-player?sponsor=1",
|
||||
"contributors": [
|
||||
|
@ -45,11 +47,17 @@
|
|||
"ytdl-core": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.13.16",
|
||||
"@babel/core": "^7.13.16",
|
||||
"@babel/preset-env": "^7.13.15",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@discord-player/extractor": "^2.0.0",
|
||||
"@discordjs/opus": "^0.5.0",
|
||||
"@types/node": "^14.14.41",
|
||||
"@types/ws": "^7.4.1",
|
||||
"discord.js": "^12.5.3",
|
||||
"discord.js-docgen": "discordjs/docgen#ts-patch",
|
||||
"jsdoc-babel": "^0.5.0",
|
||||
"prettier": "^2.2.1",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
|
|
103
src/Player.ts
103
src/Player.ts
|
@ -28,13 +28,23 @@ const SoundCloud = new SoundCloudClient();
|
|||
export class Player extends EventEmitter {
|
||||
/**
|
||||
* The discord client that instantiated this player
|
||||
* @type {Discord.Client}
|
||||
*/
|
||||
public client!: Client;
|
||||
|
||||
/**
|
||||
* The player options
|
||||
*/
|
||||
public options: PlayerOptionsType;
|
||||
|
||||
/**
|
||||
* The audio filters
|
||||
*/
|
||||
public filters: typeof AudioFilters;
|
||||
|
||||
/**
|
||||
* The collection of queues in this player
|
||||
* @type {Discord.Collection}
|
||||
*/
|
||||
public queues = new Collection<Snowflake, Queue>();
|
||||
private _resultsCollectors = new Collection<string, Collector<Snowflake, Message>>();
|
||||
|
@ -42,13 +52,14 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* The extractor model collection
|
||||
* @type {Discord.Collection}
|
||||
*/
|
||||
public Extractors = new Collection<string, ExtractorModel>();
|
||||
|
||||
/**
|
||||
* Creates new Player instance
|
||||
* @param client The discord.js client
|
||||
* @param options Player options
|
||||
* @param {Discord.Client} client The discord.js client
|
||||
* @param {PlayerOptionsType} options Player options
|
||||
*/
|
||||
constructor(client: Client, options?: PlayerOptionsType) {
|
||||
super();
|
||||
|
@ -58,17 +69,11 @@ export class Player extends EventEmitter {
|
|||
enumerable: false
|
||||
});
|
||||
|
||||
/**
|
||||
* The player options
|
||||
*/
|
||||
this.options = Object.assign({}, PlayerOptions, options ?? {});
|
||||
|
||||
// check FFmpeg
|
||||
void Util.alertFFmpeg();
|
||||
|
||||
/**
|
||||
* The audio filters
|
||||
*/
|
||||
this.filters = AudioFilters;
|
||||
|
||||
this.client.on('voiceStateUpdate', (o, n) => void this._handleVoiceStateUpdate(o, n));
|
||||
|
@ -86,8 +91,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Define custom extractor in this player
|
||||
* @param extractorName The extractor name
|
||||
* @param extractor The extractor itself
|
||||
* @param {string} extractorName The extractor name
|
||||
* @param {any} extractor The extractor itself
|
||||
*/
|
||||
use(extractorName: string, extractor: any): Player {
|
||||
if (!extractorName) throw new PlayerError('Missing extractor name!', 'PlayerExtractorError');
|
||||
|
@ -106,7 +111,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Remove existing extractor from this player
|
||||
* @param extractorName The extractor name
|
||||
* @param {string} extractorName The extractor name
|
||||
*/
|
||||
unuse(extractorName: string): boolean {
|
||||
if (!extractorName) throw new PlayerError('Missing extractor name!', 'PlayerExtractorError');
|
||||
|
@ -359,9 +364,9 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Play a song
|
||||
* @param message The discord.js message object
|
||||
* @param query Search query, can be `Player.Track` instance
|
||||
* @param firstResult If it should play the first result
|
||||
* @param {Discord.Message} message The discord.js message object
|
||||
* @param {string|Track} query Search query, can be `Player.Track` instance
|
||||
* @param {boolean} [firstResult] If it should play the first result
|
||||
* @example await player.play(message, "never gonna give you up", true)
|
||||
*/
|
||||
async play(message: Message, query: string | Track, firstResult?: boolean): Promise<void> {
|
||||
|
@ -444,7 +449,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Checks if this player is playing in a server
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
isPlaying(message: Message): boolean {
|
||||
return this.queues.some((g) => g.guildID === message.guild.id);
|
||||
|
@ -452,7 +457,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Returns guild queue object
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
getQueue(message: Message): Queue {
|
||||
return this.queues.find((g) => g.guildID === message.guild.id);
|
||||
|
@ -460,8 +465,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Sets audio filters in this player
|
||||
* @param message The message object
|
||||
* @param newFilters Audio filters object
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {QueueFilters} newFilters Audio filters object
|
||||
*/
|
||||
setFilters(message: Message, newFilters: QueueFilters): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
|
@ -495,9 +500,9 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Sets track position
|
||||
* @param message The message object
|
||||
* @param time Time in ms to set
|
||||
* @alias seek
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {number} time Time in ms to set
|
||||
* @alias Player.seek
|
||||
*/
|
||||
setPosition(message: Message, time: number): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
|
@ -519,9 +524,9 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Sets track position
|
||||
* @param message The message object
|
||||
* @param time Time in ms to set
|
||||
* @alias setPosition
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {number} time Time in ms to set
|
||||
* @alias Player.setPosition
|
||||
*/
|
||||
seek(message: Message, time: number): Promise<void> {
|
||||
return this.setPosition(message, time);
|
||||
|
@ -529,7 +534,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Skips current track
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
skip(message: Message): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -550,8 +555,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Moves to a new voice channel
|
||||
* @param message The message object
|
||||
* @param channel New voice channel to move to
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {Discord.VoiceChannel} channel New voice channel to move to
|
||||
*/
|
||||
moveTo(message: Message, channel?: VoiceChannel): boolean {
|
||||
if (!channel || channel.type !== 'voice') return;
|
||||
|
@ -577,7 +582,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Pause the playback
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
pause(message: Message): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -597,7 +602,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Resume the playback
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
resume(message: Message): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -617,7 +622,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Stops the player
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
stop(message: Message): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -641,8 +646,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Sets music volume
|
||||
* @param message The message object
|
||||
* @param percent The volume percentage/amount to set
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {number} percent The volume percentage/amount to set
|
||||
*/
|
||||
setVolume(message: Message, percent: number): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -663,7 +668,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Clears the queue
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
clearQueue(message: Message): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -679,7 +684,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Plays previous track
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
back(message: Message): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -701,8 +706,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Sets repeat mode
|
||||
* @param message The message object
|
||||
* @param enabled If it should enable the repeat mode
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {boolean} enabled If it should enable the repeat mode
|
||||
*/
|
||||
setRepeatMode(message: Message, enabled: boolean): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -718,8 +723,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Sets loop mode
|
||||
* @param message The message object
|
||||
* @param enabled If it should enable the loop mode
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {boolean} enabled If it should enable the loop mode
|
||||
*/
|
||||
setLoopMode(message: Message, enabled: boolean): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -735,7 +740,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Returns currently playing track
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
nowPlaying(message: Message): Track {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -749,7 +754,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Shuffles the queue
|
||||
* @param message The message object
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
shuffle(message: Message): Queue {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -772,8 +777,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Removes specified track
|
||||
* @param message The message object
|
||||
* @param track The track object/id to remove
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {Track|number} track The track object/id to remove
|
||||
*/
|
||||
remove(message: Message, track: Track | number): Track {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -800,8 +805,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Returns time code of currently playing song
|
||||
* @param message The message object
|
||||
* @param queueTime If it should make the time code of the whole queue
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {boolean} [queueTime] If it should make the time code of the whole queue
|
||||
*/
|
||||
getTimeCode(message: Message, queueTime?: boolean): { current: string; end: string } {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -824,8 +829,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Creates progressbar
|
||||
* @param message The message object
|
||||
* @param options Progressbar options
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {PlayerProgressbarOptions} [options] Progressbar options
|
||||
*/
|
||||
createProgressBar(message: Message, options?: PlayerProgressbarOptions): string {
|
||||
const queue = this.getQueue(message);
|
||||
|
@ -872,7 +877,7 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Gets lyrics of a song
|
||||
* @param query Search query
|
||||
* @param {string} query Search query
|
||||
* @example const lyrics = await player.lyrics("alan walker faded")
|
||||
* message.channel.send(lyrics.lyrics);
|
||||
*/
|
||||
|
@ -888,8 +893,8 @@ export class Player extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Toggle autoplay for youtube streams
|
||||
* @param message The message object
|
||||
* @param enable Enable/Disable autoplay
|
||||
* @param {Discord.Message} message The message object
|
||||
* @param {boolean} enable Enable/Disable autoplay
|
||||
*/
|
||||
setAutoPlay(message: Message, enable: boolean): boolean {
|
||||
const queue = this.getQueue(message);
|
||||
|
|
|
@ -6,12 +6,13 @@ class ExtractorModel {
|
|||
|
||||
/**
|
||||
* Model for raw Discord Player extractors
|
||||
* @param extractorName Name of the extractor
|
||||
* @param data Extractor object
|
||||
* @param {string} extractorName Name of the extractor
|
||||
* @param {Object} data Extractor object
|
||||
*/
|
||||
constructor(extractorName: string, data: any) {
|
||||
/**
|
||||
* The extractor name
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = extractorName;
|
||||
|
||||
|
@ -20,7 +21,7 @@ class ExtractorModel {
|
|||
|
||||
/**
|
||||
* Method to handle requests from `Player.play()`
|
||||
* @param query Query to handle
|
||||
* @param {string} query Query to handle
|
||||
*/
|
||||
async handle(query: string): Promise<ExtractorModelData> {
|
||||
const data = await this._raw.getInfo(query);
|
||||
|
@ -40,7 +41,7 @@ class ExtractorModel {
|
|||
|
||||
/**
|
||||
* Method used by Discord Player to validate query with this extractor
|
||||
* @param query The query to validate
|
||||
* @param {string} query The query to validate
|
||||
*/
|
||||
validate(query: string): boolean {
|
||||
return Boolean(this._raw.validate(query));
|
||||
|
@ -48,6 +49,7 @@ class ExtractorModel {
|
|||
|
||||
/**
|
||||
* The extractor version
|
||||
* @type {string}
|
||||
*/
|
||||
get version(): string {
|
||||
return this._raw.version ?? '0.0.0';
|
||||
|
@ -55,6 +57,7 @@ class ExtractorModel {
|
|||
|
||||
/**
|
||||
* If player should mark this extractor as important
|
||||
* @type {boolean}
|
||||
*/
|
||||
get important(): boolean {
|
||||
return Boolean(this._raw.important);
|
||||
|
|
|
@ -24,12 +24,16 @@ export class Queue extends EventEmitter {
|
|||
public filters: QueueFilters;
|
||||
public additionalStreamTime: number;
|
||||
public firstMessage: Message;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
public autoPlay = false;
|
||||
|
||||
/**
|
||||
* Queue constructor
|
||||
* @param player The player that instantiated this Queue
|
||||
* @param message The message object
|
||||
* @param {Player} player The player that instantiated this Queue
|
||||
* @param {Discord.Message} message The message object
|
||||
*/
|
||||
constructor(player: Player, message: Message) {
|
||||
super();
|
||||
|
@ -38,66 +42,79 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* ID of the guild assigned to this queue
|
||||
* @type {Discord.Snowflake}
|
||||
*/
|
||||
this.guildID = message.guild.id;
|
||||
|
||||
/**
|
||||
* The voice connection of this queue
|
||||
* @type {Discord.VoiceConnection}
|
||||
*/
|
||||
this.voiceConnection = null;
|
||||
|
||||
/**
|
||||
* Tracks of this queue
|
||||
* @type {Track[]}
|
||||
*/
|
||||
this.tracks = [];
|
||||
|
||||
/**
|
||||
* Previous tracks of this queue
|
||||
* @type {Track[]}
|
||||
*/
|
||||
this.previousTracks = [];
|
||||
|
||||
/**
|
||||
* If the player of this queue is stopped
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.stopped = false;
|
||||
|
||||
/**
|
||||
* If last track was skipped
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.lastSkipped = false;
|
||||
|
||||
/**
|
||||
* Queue volume
|
||||
* @type {number}
|
||||
*/
|
||||
this.volume = 100;
|
||||
|
||||
/**
|
||||
* If the player of this queue is paused
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.paused = Boolean(this.voiceConnection?.dispatcher?.paused);
|
||||
|
||||
/**
|
||||
* If repeat mode is enabled in this queue
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.repeatMode = false;
|
||||
|
||||
/**
|
||||
* If loop mode is enabled in this queue
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.loopMode = false;
|
||||
|
||||
/**
|
||||
* The additional calculated stream time
|
||||
* @type {number}
|
||||
*/
|
||||
this.additionalStreamTime = 0;
|
||||
|
||||
/**
|
||||
* The initial message object
|
||||
* @type {Discord.Message}
|
||||
*/
|
||||
this.firstMessage = message;
|
||||
|
||||
/**
|
||||
* The audio filters in this queue
|
||||
* @type {QueueFilters}
|
||||
*/
|
||||
this.filters = {};
|
||||
|
||||
|
@ -108,6 +125,7 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Currently playing track
|
||||
* @type {Track}
|
||||
*/
|
||||
get playing(): Track {
|
||||
return this.tracks[0];
|
||||
|
@ -115,6 +133,7 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Calculated volume of this queue
|
||||
* @type {number}
|
||||
*/
|
||||
get calculatedVolume(): number {
|
||||
return this.filters.normalizer ? this.volume + 70 : this.volume;
|
||||
|
@ -122,6 +141,7 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Total duration
|
||||
* @type {number}
|
||||
*/
|
||||
get totalTime(): number {
|
||||
return this.tracks.length > 0 ? this.tracks.map((t) => t.durationMS).reduce((p, c) => p + c) : 0;
|
||||
|
@ -129,6 +149,7 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Current stream time
|
||||
* @type {number}
|
||||
*/
|
||||
get currentStreamTime(): number {
|
||||
return this.voiceConnection?.dispatcher?.streamTime + this.additionalStreamTime || 0;
|
||||
|
@ -136,7 +157,8 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Sets audio filters in this player
|
||||
* @param filters Audio filters to set
|
||||
* @param {QueueFilters} filters Audio filters to set
|
||||
* @type {Promise<void>}
|
||||
*/
|
||||
setFilters(filters: QueueFilters): Promise<void> {
|
||||
return this.player.setFilters(this.firstMessage, filters);
|
||||
|
@ -144,6 +166,7 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Returns array of all enabled filters
|
||||
* @type {string[]}
|
||||
*/
|
||||
getFiltersEnabled(): string[] {
|
||||
const filters: string[] = [];
|
||||
|
@ -157,6 +180,7 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Returns all disabled filters
|
||||
* @type {string[]}
|
||||
*/
|
||||
getFiltersDisabled(): string[] {
|
||||
const enabled = this.getFiltersEnabled();
|
||||
|
@ -166,6 +190,7 @@ export class Queue extends EventEmitter {
|
|||
|
||||
/**
|
||||
* String representation of this Queue
|
||||
* @type {string}
|
||||
*/
|
||||
toString(): string {
|
||||
return `<Queue ${this.guildID}>`;
|
||||
|
|
|
@ -6,63 +6,74 @@ import Queue from './Queue';
|
|||
export class Track {
|
||||
/**
|
||||
* The player that instantiated this Track
|
||||
* @type {Player}
|
||||
*/
|
||||
public player!: Player;
|
||||
|
||||
/**
|
||||
* Title of this track
|
||||
* @type {string}
|
||||
*/
|
||||
public title!: string;
|
||||
|
||||
/**
|
||||
* Description of this track
|
||||
* @type {string}
|
||||
*/
|
||||
public description!: string;
|
||||
|
||||
/**
|
||||
* Author of this track
|
||||
* @type {string}
|
||||
*/
|
||||
public author!: string;
|
||||
|
||||
/**
|
||||
* Link of this track
|
||||
* @type {string}
|
||||
*/
|
||||
public url!: string;
|
||||
|
||||
/**
|
||||
* Thumbnail of this track
|
||||
* @type {string}
|
||||
*/
|
||||
public thumbnail!: string;
|
||||
|
||||
/**
|
||||
* Duration of this track
|
||||
* @type {string}
|
||||
*/
|
||||
public duration!: string;
|
||||
|
||||
/**
|
||||
* View count of this track
|
||||
* @type {number}
|
||||
*/
|
||||
public views!: number;
|
||||
|
||||
/**
|
||||
* Person who requested this track
|
||||
* @type {Discord.User}
|
||||
*/
|
||||
public requestedBy!: User;
|
||||
|
||||
/**
|
||||
* If this track belongs to a playlist
|
||||
* @type {boolean}
|
||||
*/
|
||||
public fromPlaylist!: boolean;
|
||||
|
||||
/**
|
||||
* Raw data of this track
|
||||
* @type {TrackData}
|
||||
*/
|
||||
public raw!: TrackData;
|
||||
|
||||
/**
|
||||
* Track constructor
|
||||
* @param player The player that instantiated this Track
|
||||
* @param data Track data
|
||||
* @param {Player} player The player that instantiated this Track
|
||||
* @param {TrackData} data Track data
|
||||
*/
|
||||
constructor(player: Player, data: TrackData) {
|
||||
Object.defineProperty(this, 'player', { value: player, enumerable: false });
|
||||
|
@ -87,6 +98,7 @@ export class Track {
|
|||
|
||||
/**
|
||||
* The queue in which this track is located
|
||||
* @type {Queue}
|
||||
*/
|
||||
get queue(): Queue {
|
||||
return this.player.queues.find((q) => q.tracks.includes(this));
|
||||
|
@ -94,6 +106,7 @@ export class Track {
|
|||
|
||||
/**
|
||||
* The track duration in millisecond
|
||||
* @type {number}
|
||||
*/
|
||||
get durationMS(): number {
|
||||
const times = (n: number, t: number) => {
|
||||
|
@ -111,6 +124,7 @@ export class Track {
|
|||
|
||||
/**
|
||||
* String representation of this track
|
||||
* @type {string}
|
||||
*/
|
||||
toString(): string {
|
||||
return `${this.title} by ${this.author}`;
|
||||
|
|
|
@ -47,11 +47,7 @@ const FilterList = {
|
|||
return `${Object.values(this).join(',')}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates single string of audio filters
|
||||
* @param filter Array of AudioFilters name
|
||||
*/
|
||||
create(filter?: FiltersName[]) {
|
||||
create(filter?: FiltersName[]): string {
|
||||
if (!filter || !Array.isArray(filter)) return this.toString();
|
||||
return filter
|
||||
.filter((predicate) => typeof predicate === 'string')
|
||||
|
@ -59,27 +55,13 @@ const FilterList = {
|
|||
.join(',');
|
||||
},
|
||||
|
||||
/**
|
||||
* Defines custom filter
|
||||
* @param filterName The filter name
|
||||
* @param value FFmpeg args to use with -af
|
||||
* @example Player.AudioFilters.define("3D", "apulsator=hz=0.125")
|
||||
*
|
||||
* player.setFilters(message, { "3D": true })
|
||||
*/
|
||||
define(filterName: string, value: string) {
|
||||
/* @ts-ignore */
|
||||
if (typeof this[filterName] && typeof this[filterName] === 'function') return;
|
||||
define(filterName: string, value: string): void {
|
||||
if (typeof this[filterName as FiltersName] && typeof this[filterName as FiltersName] === 'function') return;
|
||||
|
||||
/* @ts-ignore */
|
||||
this[filterName] = value;
|
||||
this[filterName as FiltersName] = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Defines filters in bulk
|
||||
* @param filterArray Array of filters containing object with `name` and `value` prop
|
||||
*/
|
||||
defineBulk(filterArray: { name: string; value: string }[]) {
|
||||
defineBulk(filterArray: { name: string; value: string }[]): void {
|
||||
filterArray.forEach((arr) => this.define(arr.name, arr.value));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,10 +15,17 @@ const reverbnationRegex = /https:\/\/(www.)?reverbnation.com\/(.+)\/song\/(.+)/;
|
|||
const attachmentRegex = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/;
|
||||
|
||||
export class Util {
|
||||
/**
|
||||
* Static Player Util class
|
||||
*/
|
||||
constructor() {
|
||||
throw new Error(`The ${this.constructor.name} class is static and cannot be instantiated!`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks FFmpeg Version
|
||||
* @param {boolean} [force] If it should forcefully get the version
|
||||
*/
|
||||
static getFFmpegVersion(force?: boolean): string {
|
||||
try {
|
||||
const info = FFmpeg.getInfo(Boolean(force));
|
||||
|
@ -29,11 +36,18 @@ export class Util {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks FFmpeg
|
||||
* @param {boolean} [force] If it should forcefully get the version
|
||||
*/
|
||||
static checkFFmpeg(force?: boolean): boolean {
|
||||
const version = Util.getFFmpegVersion(force);
|
||||
return version === null ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alerts if FFmpeg is not available
|
||||
*/
|
||||
static alertFFmpeg(): void {
|
||||
const hasFFmpeg = Util.checkFFmpeg();
|
||||
|
||||
|
@ -43,6 +57,10 @@ export class Util {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves query type
|
||||
* @param {string} query The query
|
||||
*/
|
||||
static getQueryType(query: string): QueryType {
|
||||
if (SoundcloudValidateURL(query) && !query.includes('/sets/')) return 'soundcloud_track';
|
||||
if (SoundcloudValidateURL(query) && query.includes('/sets/')) return 'soundcloud_playlist';
|
||||
|
@ -59,10 +77,18 @@ export class Util {
|
|||
return 'youtube_search';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given string is url
|
||||
* @param {string} str URL to check
|
||||
*/
|
||||
static isURL(str: string): boolean {
|
||||
return str.length < 2083 && attachmentRegex.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Vimeo ID
|
||||
* @param {string} query Vimeo link
|
||||
*/
|
||||
static getVimeoID(query: string): string {
|
||||
return Util.getQueryType(query) === 'vimeo'
|
||||
? query
|
||||
|
@ -72,6 +98,10 @@ export class Util {
|
|||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses ms time
|
||||
* @param {number} milliseconds Time to parse
|
||||
*/
|
||||
static parseMS(milliseconds: number): TimeData {
|
||||
const roundTowardsZero = milliseconds > 0 ? Math.floor : Math.ceil;
|
||||
|
||||
|
@ -83,12 +113,22 @@ export class Util {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates simple duration string
|
||||
* @param {object} durObj Duration object
|
||||
*/
|
||||
static durationString(durObj: object): string {
|
||||
return Object.values(durObj)
|
||||
.map((m) => (isNaN(m) ? 0 : m))
|
||||
.join(':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes youtube searches
|
||||
* @param {string} query The query
|
||||
* @param {any} options Options
|
||||
* @returns {Promise<Track[]>}
|
||||
*/
|
||||
static ytSearch(query: string, options?: any): Promise<Track[]> {
|
||||
return new Promise(async (resolve) => {
|
||||
await YouTube.search(query, {
|
||||
|
@ -119,6 +159,9 @@ export class Util {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this system is running in replit.com
|
||||
*/
|
||||
static isRepl(): boolean {
|
||||
if ('DP_REPL_NOCHECK' in process.env) return false;
|
||||
|
||||
|
@ -137,10 +180,18 @@ export class Util {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given voice channel is empty
|
||||
* @param {Discord.VoiceChannel} channel The voice channel
|
||||
*/
|
||||
static isVoiceEmpty(channel: VoiceChannel): boolean {
|
||||
return channel.members.filter((member) => !member.user.bot).size === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds time code
|
||||
* @param {object} data The data to build time code from
|
||||
*/
|
||||
static buildTimeCode(data: any): string {
|
||||
const items = Object.keys(data);
|
||||
const required = ['days', 'hours', 'minutes', 'seconds'];
|
||||
|
@ -155,7 +206,7 @@ export class Util {
|
|||
|
||||
/**
|
||||
* Manage CJS require
|
||||
* @param id id to require
|
||||
* @param {string} id id to require
|
||||
*/
|
||||
static require(id: string): any {
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue