Comparing version 3.0.2 to 3.1.0
26
index.js
@@ -1,16 +0,12 @@ | ||
module.exports = { | ||
Node: require("./src/Node"), | ||
Player: require("./src/Player"), | ||
Poru: require("./src/Poru"), | ||
Spotify : require("./src/platform/Spotify"), | ||
Deezer : require("./src/platform/Deezer"), | ||
AppleMusic : require("./src/platform/AppleMusic"), | ||
Queue : require("./src/guild/Queue"), | ||
Filter : require("./src/guild/Filter"), | ||
Track: require("./src/guild/Track"), | ||
PoruTrack : require("./src/guild/PoruTrack") | ||
} | ||
Node: require("./src/Node"), | ||
Player: require("./src/Player"), | ||
Poru: require("./src/Poru"), | ||
Spotify: require("./src/platform/Spotify"), | ||
Deezer: require("./src/platform/Deezer"), | ||
AppleMusic: require("./src/platform/AppleMusic"), | ||
Queue: require("./src/guild/Queue"), | ||
Filter: require("./src/guild/Filter"), | ||
Track: require("./src/guild/Track"), | ||
PoruTrack: require("./src/guild/PoruTrack"), | ||
}; |
{ | ||
"name": "poru", | ||
"version": "3.0.2", | ||
"description": "A stable and powefull lavalink client with so many features", | ||
"version": "3.1.0", | ||
"description": "A stable and powerfull lavalink client around node.js", | ||
"main": "index.js", | ||
@@ -24,4 +24,4 @@ "scripts": { | ||
"engines": { | ||
"node": ">=16.9.0" | ||
}, | ||
"node": ">=16.9.0" | ||
}, | ||
"keywords": [ | ||
@@ -35,2 +35,3 @@ "music", | ||
"soundcloud", | ||
"deezer", | ||
"discordjs", | ||
@@ -37,0 +38,0 @@ "eris" |
@@ -53,4 +53,4 @@ <p align="center"> | ||
```javascript | ||
const { Client, GatewayIntentBits } = require('discord.js'); | ||
const { Poru } = require('poru'); | ||
const { Client, GatewayIntentBits } = require("discord.js"); | ||
const { Poru } = require("poru"); | ||
const nodes = [ | ||
@@ -61,22 +61,22 @@ { | ||
port: 8080, | ||
password: "iloveyou3000" | ||
} | ||
] | ||
password: "iloveyou3000", | ||
}, | ||
]; | ||
const PoruOptions = { | ||
reconnectTime: 0, | ||
resumeKey: 'MyPlayers', | ||
resumeKey: "MyPlayers", | ||
resumeTimeout: 60, | ||
defaultPlatform: "ytsearch" | ||
} | ||
defaultPlatform: "ytsearch", | ||
}; | ||
const client = new Client({ | ||
intents: [ | ||
GatewayIntentBits.Guilds, | ||
GatewayIntentBits.GuildMessages, | ||
GatewayIntentBits.GuildVoiceStates, | ||
GatewayIntentBits.MessageContent | ||
] | ||
GatewayIntentBits.Guilds, | ||
GatewayIntentBits.GuildMessages, | ||
GatewayIntentBits.GuildVoiceStates, | ||
GatewayIntentBits.MessageContent, | ||
], | ||
}); | ||
client.poru = new Poru(client, nodes, PoruOptions) | ||
client.poru = new Poru(client, nodes, PoruOptions); | ||
client.poru.on('trackStart', (player, track) => { | ||
client.poru.on("trackStart", (player, track) => { | ||
const channel = client.channels.cache.get(player.textChannel); | ||
@@ -86,13 +86,16 @@ return channel.send(`Now playing \`${track.title}\``); | ||
client.on('ready', () => { | ||
console.log('Ready!'); | ||
client.on("ready", () => { | ||
console.log("Ready!"); | ||
client.poru.init(client); | ||
}); | ||
client.on('interactionCreate', async (interaction) => { | ||
client.on("interactionCreate", async (interaction) => { | ||
if (!interaction.isChatInputCommand()) return; | ||
if (!interaction.member.voice.channel) return interaction.reply({ content: `Please connect with voice channel `, ephemeral: true }); | ||
if (!interaction.member.voice.channel) | ||
return interaction.reply({ | ||
content: `Please connect with voice channel `, | ||
ephemeral: true, | ||
}); | ||
const track = interaction.options.getString('track'); | ||
const track = interaction.options.getString("track"); | ||
@@ -102,8 +105,8 @@ const res = await client.poru.resolve(track); | ||
if (res.loadType === "LOAD_FAILED") { | ||
return interaction.reply('Failed to load track.'); | ||
return interaction.reply("Failed to load track."); | ||
} else if (res.loadType === "NO_MATCHES") { | ||
return interaction.reply('No source found!'); | ||
return interaction.reply("No source found!"); | ||
} | ||
//create connection with discord voice channnel | ||
//create connection with discord voice channnel | ||
const player = client.poru.createConnection({ | ||
@@ -113,7 +116,6 @@ guildId: interaction.guild.id, | ||
textChannel: interaction.channel.id, | ||
selfDeaf: true | ||
selfDeaf: true, | ||
}); | ||
if (res.loadType === 'PLAYLIST_LOADED') { | ||
if (res.loadType === "PLAYLIST_LOADED") { | ||
for (const track of res.tracks) { | ||
@@ -124,3 +126,5 @@ track.info.requester = interaction.user; | ||
interaction.reply(`${res.playlistInfo.name} has been loaded with ${res.tracks.length}`); | ||
interaction.reply( | ||
`${res.playlistInfo.name} has been loaded with ${res.tracks.length}` | ||
); | ||
} else { | ||
@@ -136,3 +140,3 @@ const track = res.tracks[0]; | ||
client.login('TOKEN'); | ||
client.login("TOKEN"); | ||
``` | ||
@@ -139,0 +143,0 @@ |
module.exports = { | ||
clientName: "Poru", | ||
autoResume : true, | ||
version :"2.0", | ||
OPCodes: { | ||
CONFIGURE_RESUMING : 'configureResuming', | ||
DESTROY : 'destroy', | ||
FILTERS : 'filters', | ||
EVENT : 'event', | ||
PAUSE : 'pause', | ||
PLAY : 'play', | ||
PLAYER_UPDATE : 'playerUpdate', | ||
SEEK : 'seek', | ||
STATS : 'stats', | ||
STOP : 'stop', | ||
VOICE_UPDATE : 'voiceUpdate', | ||
VOLUME : 'volume' | ||
} | ||
} | ||
clientName: "Poru", | ||
autoResume: true, | ||
version: "2.0", | ||
OPCodes: { | ||
CONFIGURE_RESUMING: "configureResuming", | ||
DESTROY: "destroy", | ||
FILTERS: "filters", | ||
EVENT: "event", | ||
PAUSE: "pause", | ||
PLAY: "play", | ||
PLAYER_UPDATE: "playerUpdate", | ||
SEEK: "seek", | ||
STATS: "stats", | ||
STOP: "stop", | ||
VOICE_UPDATE: "voiceUpdate", | ||
VOLUME: "volume", | ||
}, | ||
}; |
class Filters { | ||
constructor(player, options = {}) { | ||
this._8d = options._8d || null; | ||
this.bassboost = options.bassboost || null; | ||
this.player = player; | ||
this.node = player.node; | ||
this.equalizer = options.equalizer || []; | ||
this.karaoke = options.karaoke || null; | ||
this.timescale = options.timescale || null; | ||
this.tremolo = options.tremolo || null; | ||
this.vibrato = options.vibrato || null; | ||
this.rotation = options.rotation || null; | ||
this.distortion = options.distortion || null; | ||
this.channelMix = options.channelMix || null; | ||
this.lowPass = options.lowPass || null; | ||
} | ||
constructor(player,options = {}) { | ||
this._8d =options._8d|| null; | ||
this.bassboost = options.bassboost || null; | ||
this.player = player; | ||
this.node = player.node | ||
this.equalizer = options.equalizer || []; | ||
this.karaoke = options.karaoke || null; | ||
this.timescale = options.timescale || null; | ||
this.tremolo = options.tremolo || null; | ||
this.vibrato = options.vibrato || null; | ||
this.rotation = options.rotation || null; | ||
this.distortion = options.distortion || null; | ||
this.channelMix = options.channelMix || null; | ||
this.lowPass = options.lowPass || null; | ||
} | ||
setEqualizer(band, gain) { | ||
this.band = band || this.band; | ||
this.gain = gain || this.gain; | ||
this.equalizer = [ | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
]; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setKaraoke(karaoke) { | ||
this.karaoke = karaoke || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setTimescale(timescale) { | ||
this.timescale = timescale || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setTremolo(tremolo) { | ||
this.tremolo = tremolo || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setEqualizer(band,gain){ | ||
this.band = band || this.band; | ||
this.gain = gain || this.gain; | ||
this.equalizer = [ | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
{ | ||
band: this.band, | ||
gain: this.gain, | ||
}, | ||
] | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setKaraoke(karaoke) { | ||
this.karaoke = karaoke|| null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setTimescale(timescale) { | ||
this.timescale = timescale || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setTremolo(tremolo) { | ||
this.tremolo = tremolo || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setVibrato(vibrato) { | ||
this.vibrato = vibrato || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setVibrato(vibrato){ | ||
this.vibrato = vibrato || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setRotation(rotation) { | ||
this.rotation = rotation || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setDistortion(distortion) { | ||
this.distortion = distortion || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setRotation(rotation) { | ||
this.rotation = rotation || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setDistortion(distortion) { | ||
this.distortion = distortion || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setChannelMix(mix) { | ||
this.channelMix = mix || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setChannelMix(mix) { | ||
this.channelMix = mix || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setLowPass(pass) { | ||
this.lowPass = pass || null; | ||
this.updateFilters(); | ||
return this; | ||
} | ||
setLowPass(pass) { | ||
this.lowPass = pass || null; | ||
this.updateFilters(); | ||
return this; | ||
setFilters(options) { | ||
this.player.filters = new Filters(this.player, options); | ||
this.updateFilters(); | ||
return this; | ||
} | ||
clearFilters() { | ||
this.player.filters = new Filters(this.player); | ||
this.player.node.send({ | ||
op: "filters", | ||
guildId: this.player.guildId, | ||
}); | ||
return this; | ||
} | ||
setNightcore(val) { | ||
if (!this.player) return; | ||
this.setTimescale(val ? { rate: 1.5 } : null); | ||
this.nightcore = val; | ||
if (val) { | ||
this.doubleTime = false; | ||
this.vaporwave = false; | ||
} | ||
return val; | ||
} | ||
setFilters(options) { | ||
this.player.filters = new Filters(this.player,options); | ||
this.updateFilters(); | ||
return this; | ||
setSlowmode(val) { | ||
if (!this.player) return; | ||
this.slowmode = val; | ||
if (val) { | ||
} | ||
this.setFilters( | ||
val | ||
? { | ||
timescale: { | ||
speed: 0.5, | ||
pitch: 1.0, | ||
rate: 0.8, | ||
}, | ||
} | ||
: this.clearFilters() | ||
); | ||
} | ||
clearFilters() { | ||
this.player.filters = new Filters(this.player); | ||
this.player.node.send({ | ||
op: "filters", | ||
guildId: this.player.guildId | ||
}); | ||
return this; | ||
setVaporwave(val) { | ||
if (!this.player) return; | ||
this.vaporwave = val; | ||
if (val) { | ||
this.doubleTime = false; | ||
this.nightcore = false; | ||
} | ||
setNightcore(val) { | ||
if (!this.player) return; | ||
this.setTimescale(val ? { rate: 1.5 } : null); | ||
this.nightcore = val; | ||
if (val) { | ||
this.doubleTime = false; | ||
this.vaporwave = false; | ||
} | ||
return val; | ||
} | ||
this.setTimescale(val ? { pitch: 0.5 } : null); | ||
} | ||
setSlowmode(val) { | ||
if (!this.player) return; | ||
this.slowmode = val; | ||
if (val) { | ||
} | ||
this.setFilters( | ||
val | ||
? { | ||
timescale: { | ||
speed: 0.5, | ||
pitch: 1.0, | ||
rate: 0.8, | ||
}, | ||
} | ||
: this.clearFilters() | ||
); | ||
} | ||
set8D(val) { | ||
if (!this.player) return; | ||
this._8d = val; | ||
this.setRotation(val ? { rotationHz: 0.2 } : null); | ||
} | ||
setVaporwave(val) { | ||
if (!this.player) return; | ||
this.vaporwave = val; | ||
if (val) { | ||
this.doubleTime = false; | ||
this.nightcore = false; | ||
} | ||
this.setTimescale(val ? { pitch: 0.5 } : null); | ||
} | ||
set8D(val) { | ||
if (!this.player) return; | ||
this._8d = val; | ||
this.setRotation(val ? { rotationHz: 0.2 } : null); | ||
} | ||
setBassboost(val) { | ||
if (!this.player) return; | ||
this.bassboost = !!val; | ||
this.bassboost = val / 100; | ||
this.setEqualizer(1, 0.9); | ||
} | ||
setBassboost(val) { | ||
if (!this.player) return; | ||
this.bassboost = !!val; | ||
this.bassboost = val / 100; | ||
this.setEqualizer(1,0.90); | ||
} | ||
updateFilters(){ | ||
const {equalizer, karaoke, timescale, tremolo, vibrato, rotation, distortion, channelMix, lowPass } = this; | ||
this.node.send({ | ||
op:"filters", | ||
guildId: this.player.guildId, | ||
equalizer, | ||
karaoke, | ||
timescale, | ||
tremolo, | ||
vibrato, | ||
rotation, | ||
distortion, | ||
channelMix, | ||
lowPass | ||
}); | ||
} | ||
updateFilters() { | ||
const { | ||
equalizer, | ||
karaoke, | ||
timescale, | ||
tremolo, | ||
vibrato, | ||
rotation, | ||
distortion, | ||
channelMix, | ||
lowPass, | ||
} = this; | ||
this.node.send({ | ||
op: "filters", | ||
guildId: this.player.guildId, | ||
equalizer, | ||
karaoke, | ||
timescale, | ||
tremolo, | ||
vibrato, | ||
rotation, | ||
distortion, | ||
channelMix, | ||
lowPass, | ||
}); | ||
} | ||
} | ||
module.exports = Filters; |
@@ -1,5 +0,5 @@ | ||
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | ||
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
class PoruTrack { | ||
constructor(data) { | ||
this.track = data.track | ||
this.track = data.track; | ||
this.info = { | ||
@@ -14,13 +14,15 @@ identifier: null, | ||
uri: data.info.uri, | ||
image: data.info.image || null | ||
} | ||
image: data.info.image || null, | ||
}; | ||
} | ||
async resolve(manager) { | ||
const query = [this.info.author, this.info.title] | ||
.filter((x) => !!x) | ||
.join(" - "); | ||
const query = [this.info.author, this.info.title].filter((x) => !!x).join(' - '); | ||
const result = await manager.resolve(query,manager.options.defaultPlatform || "ytsearch"); | ||
const result = await manager.resolve( | ||
query, | ||
manager.options.defaultPlatform || "ytsearch" | ||
); | ||
if (!result || !result.tracks.length) return; | ||
@@ -32,10 +34,14 @@ | ||
(track) => | ||
author.some((name) => new RegExp(`^${escapeRegExp(name)}$`, 'i').test(track.info.author)) || | ||
new RegExp(`^${escapeRegExp(this.info.title)}$`, 'i').test(track.info.title), | ||
author.some((name) => | ||
new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info.author) | ||
) || | ||
new RegExp(`^${escapeRegExp(this.info.title)}$`, "i").test( | ||
track.info.title | ||
) | ||
); | ||
if (officialAudio) { | ||
this.info.identifier = officialAudio.info.identifier | ||
this.info.identifier = officialAudio.info.identifier; | ||
this.track = officialAudio.track; | ||
this.info.length = officialAudio.info.length | ||
return this | ||
this.info.length = officialAudio.info.length; | ||
return this; | ||
} | ||
@@ -47,15 +53,15 @@ } | ||
track.info.length >= (this.info.length ? this.length : 0) - 2000 && | ||
track.info.length <= (this.info.length ? this.length : 0) + 2000, | ||
track.info.length <= (this.info.length ? this.length : 0) + 2000 | ||
); | ||
if (sameDuration) { | ||
this.info.identifier = sameDuration.info.identifier | ||
this.info.identifier = sameDuration.info.identifier; | ||
this.track = sameDuration.track; | ||
this.info.length = sameDuration.length | ||
return this | ||
this.info.length = sameDuration.length; | ||
return this; | ||
} | ||
} | ||
this.info.identifier = result.tracks[0].info.identifier | ||
this.info.identifier = result.tracks[0].info.identifier; | ||
this.track = result.tracks[0].track; | ||
this.info.length = result.tracks[0].info.length | ||
return this | ||
this.info.length = result.tracks[0].info.length; | ||
return this; | ||
} | ||
@@ -62,0 +68,0 @@ } |
@@ -1,37 +0,35 @@ | ||
class Queue extends Array{ | ||
constructor() { | ||
super(...arguments); | ||
} | ||
get size() { | ||
return this.length | ||
} | ||
first() { | ||
return this ? this[0] : 0; | ||
} | ||
class Queue extends Array { | ||
constructor() { | ||
super(...arguments); | ||
} | ||
add(track) { | ||
this.push(track); | ||
return this; | ||
} | ||
remove(index) { | ||
return this.splice(index, 1)[0]; | ||
} | ||
clear() { | ||
return this.splice(0); | ||
} | ||
shuffle() { | ||
for (let i = this.length - 1; i > 0; i -= 1) { | ||
const j = Math.floor(Math.random() * (i + 1)); | ||
[this[i], this[j]] = [this[j], this[i]]; | ||
} | ||
} | ||
get size() { | ||
return this.length; | ||
} | ||
first() { | ||
return this ? this[0] : 0; | ||
} | ||
add(track) { | ||
this.push(track); | ||
return this; | ||
} | ||
remove(index) { | ||
return this.splice(index, 1)[0]; | ||
} | ||
clear() { | ||
return this.splice(0); | ||
} | ||
shuffle() { | ||
for (let i = this.length - 1; i > 0; i -= 1) { | ||
const j = Math.floor(Math.random() * (i + 1)); | ||
[this[i], this[j]] = [this[j], this[i]]; | ||
} | ||
} | ||
} | ||
module.exports = Queue; |
const Track = require("./Track"); | ||
class Response { | ||
constructor(data) { | ||
this.tracks = data.tracks.map((track) => new Track(track)); | ||
this.loadType = data.loadType; | ||
this.playlistInfo = data.playlistInfo; | ||
} | ||
constructor(data) { | ||
this.tracks = data.tracks.map((track) => new Track(track)); | ||
this.loadType = data.loadType; | ||
this.playlistInfo = data.playlistInfo; | ||
} | ||
module.exports =Response | ||
} | ||
module.exports = Response; |
@@ -1,5 +0,5 @@ | ||
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | ||
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
class Track { | ||
constructor(data) { | ||
this.track = data.track | ||
this.track = data.track; | ||
this.info = { | ||
@@ -14,13 +14,17 @@ identifier: data.info.identifier, | ||
uri: data.info.uri, | ||
image: `https://i.ytimg.com/vi/${data.info.identifier}/maxresdefault.jpg` || null | ||
} | ||
image: | ||
`https://i.ytimg.com/vi/${data.info.identifier}/maxresdefault.jpg` || | ||
null, | ||
}; | ||
} | ||
async resolve(manager) { | ||
const query = [this.info.author, this.info.title] | ||
.filter((x) => !!x) | ||
.join(" - "); | ||
const query = [this.info.author, this.info.title].filter((x) => !!x).join(' - '); | ||
const result = await manager.resolve(query,manager.options.defaultPlatform || "ytsearch"); | ||
const result = await manager.resolve( | ||
query, | ||
manager.options.defaultPlatform || "ytsearch" | ||
); | ||
if (!result || !result.tracks.length) return; | ||
@@ -32,10 +36,14 @@ | ||
(track) => | ||
author.some((name) => new RegExp(`^${escapeRegExp(name)}$`, 'i').test(track.info.author)) || | ||
new RegExp(`^${escapeRegExp(this.info.title)}$`, 'i').test(track.info.title), | ||
author.some((name) => | ||
new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info.author) | ||
) || | ||
new RegExp(`^${escapeRegExp(this.info.title)}$`, "i").test( | ||
track.info.title | ||
) | ||
); | ||
if (officialAudio) { | ||
this.info.identifier = officialAudio.info.identifier | ||
this.image =`https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg` | ||
this.info.identifier = officialAudio.info.identifier; | ||
this.image = `https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg`; | ||
this.track = officialAudio.track; | ||
return this | ||
return this; | ||
} | ||
@@ -47,15 +55,15 @@ } | ||
track.info.length >= (this.info.length ? this.length : 0) - 2000 && | ||
track.info.length <= (this.info.length ? this.length : 0) + 2000, | ||
track.info.length <= (this.info.length ? this.length : 0) + 2000 | ||
); | ||
if (sameDuration) { | ||
this.info.identifier = sameDuration.info.identifier | ||
this.image =`https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg` | ||
this.info.identifier = sameDuration.info.identifier; | ||
this.image = `https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg`; | ||
this.track = sameDuration.track; | ||
return this | ||
return this; | ||
} | ||
} | ||
this.info.identifier = result.tracks[0].info.identifier | ||
this.image =`https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg` | ||
this.info.identifier = result.tracks[0].info.identifier; | ||
this.image = `https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg`; | ||
this.track = result.tracks[0].track; | ||
return this | ||
return this; | ||
} | ||
@@ -62,0 +70,0 @@ } |
322
src/Node.js
const WebSocket = require("ws"); | ||
const config = require("./config") | ||
const config = require("./config"); | ||
class Node { | ||
constructor(manager,options,node) { | ||
this.manager = manager | ||
this.name= options.name || null; | ||
this.host = options.host || "localhost" | ||
this.port = options.port || 2333 | ||
this.password = options.password ||"youshallnotpass" | ||
this.secure = options.secure || false; | ||
this.url = `${this.secure ? 'wss' : 'ws'}://${this.host}:${this.port}/`; | ||
this.ws = null; | ||
this.reconnectTime = node.reconnectTime || 5000; | ||
this.resumeKey = node.resumeKey || null; | ||
this.resumeTimeout = node.resumeTimeout || 60; | ||
this.reconnectAttempt; | ||
this.reconnects = 0; | ||
this.isConnected = false; | ||
this.destroyed = null; | ||
this.stats = { | ||
players: 0, | ||
playingPlayers: 0, | ||
uptime: 0, | ||
memory: { | ||
free: 0, | ||
used: 0, | ||
allocated: 0, | ||
reservable: 0, | ||
}, | ||
cpu: { | ||
cores: 0, | ||
systemLoad: 0, | ||
lavalinkLoad: 0, | ||
}, | ||
}; | ||
constructor(manager, options, node) { | ||
this.manager = manager; | ||
this.name = options.name || null; | ||
this.host = options.host || "localhost"; | ||
this.port = options.port || 2333; | ||
this.password = options.password || "youshallnotpass"; | ||
this.secure = options.secure || false; | ||
this.url = `${this.secure ? "wss" : "ws"}://${this.host}:${this.port}/`; | ||
this.ws = null; | ||
this.reconnectTime = node.reconnectTime || 5000; | ||
this.resumeKey = node.resumeKey || null; | ||
this.resumeTimeout = node.resumeTimeout || 60; | ||
this.reconnectAttempt; | ||
this.reconnects = 0; | ||
this.isConnected = false; | ||
this.destroyed = null; | ||
this.stats = { | ||
players: 0, | ||
playingPlayers: 0, | ||
uptime: 0, | ||
memory: { | ||
free: 0, | ||
used: 0, | ||
allocated: 0, | ||
reservable: 0, | ||
}, | ||
cpu: { | ||
cores: 0, | ||
systemLoad: 0, | ||
lavalinkLoad: 0, | ||
}, | ||
}; | ||
} | ||
connect() { | ||
if (this.ws) this.ws.close(); | ||
const headers = { | ||
Authorization: this.password, | ||
"Num-Shards": this.manager.shards || 1, | ||
"User-Id": this.manager.user, | ||
"Client-Name": config.clientName, | ||
}; | ||
if (this.resumeKey) headers["Resume-Key"] = this.resumeKey; | ||
this.ws = new WebSocket(this.url, { headers }); | ||
this.ws.on("open", this.#open.bind(this)); | ||
this.ws.on("error", this.#error.bind(this)); | ||
this.ws.on("message", this.#message.bind(this)); | ||
this.ws.on("close", this.#close.bind(this)); | ||
} | ||
} | ||
disconnect() { | ||
if (!this.isConnected) return; | ||
connect() { | ||
if (this.ws) this.ws.close(); | ||
const headers = { | ||
Authorization: this.password, | ||
"Num-Shards": this.manager.shards || 1, | ||
"User-Id": this.manager.user, | ||
"Client-Name": config.clientName | ||
}; | ||
if (this.resumeKey) headers["Resume-Key"] = this.resumeKey; | ||
this.ws = new WebSocket(this.url, { headers }); | ||
this.ws.on("open",this.#open.bind(this)); | ||
this.ws.on("error", this.#error.bind(this)); | ||
this.ws.on("message", this.#message.bind(this)); | ||
this.ws.on("close", this.#close.bind(this)); | ||
this.ws?.removeAllListeners(); | ||
this.ws?.close(); | ||
this.ws = null; | ||
this.isConnected = false; | ||
} | ||
#open() { | ||
if (this.reconnectAttempt) { | ||
clearTimeout(this.reconnectAttempt); | ||
delete this.reconnectAttempt; | ||
} | ||
disconnect(){ | ||
if(!this.isConnected) return; | ||
this.ws?.removeAllListeners(); | ||
this.ws?.close(); | ||
this.ws = null; | ||
this.isConnected = false; | ||
if (this.resumeKey) { | ||
this.send({ | ||
op: "configureResuming", | ||
key: this.resumeKey.toString(), | ||
timeout: this.resumeTimeout, | ||
}); | ||
this.manager.emit( | ||
"debug", | ||
this.name, | ||
`[Web Socket] Resuming configured on Lavalink` | ||
); | ||
} | ||
this.manager.emit("nodeConnect", this); | ||
this.isConnected = true; | ||
this.manager.emit( | ||
"debug", | ||
this.name, | ||
`[Web Socket] Connection ready ${this.url}` | ||
); | ||
#open(){ | ||
if (this.reconnectAttempt) { | ||
clearTimeout(this.reconnectAttempt); | ||
delete this.reconnectAttempt; | ||
} | ||
if (this.resumeKey){ | ||
this.send({ | ||
op: "configureResuming", | ||
key: (this.resumeKey).toString(), | ||
timeout: this.resumeTimeout | ||
}); | ||
this.manager.emit("debug",this.name,`[Web Socket] Resuming configured on Lavalink`) | ||
if (config.autoResume) { | ||
for (const player of this.manager.players.values()) { | ||
if (player.node === this) { | ||
player.restart(); | ||
} | ||
this.manager.emit("nodeConnect", this); | ||
this.isConnected = true; | ||
this.manager.emit('debug', this.name, `[Web Socket] Connection ready ${this.url}`); | ||
if(config.autoResume){ | ||
for (const player of this.manager.players.values()) { | ||
if (player.node === this) { | ||
player.restart(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
#message(payload) { | ||
#message(payload) { | ||
const packet = JSON.parse(payload); | ||
if (!packet.op) return; | ||
const packet = JSON.parse(payload); | ||
if(!packet.op) return; | ||
if (packet.op && packet.op === "stats") { | ||
this.stats = { ...packet }; | ||
} | ||
const player = this.manager.players.get(packet.guildId); | ||
if (packet.guildId && player) player.emit(packet.op, packet); | ||
packet.node = this; | ||
this.manager.emit("debug",this.name,`[Web Socket] Lavalink Node Update : ${packet.op} `) | ||
this.manager.emit("raw", packet); | ||
if (packet.op && packet.op === "stats") { | ||
this.stats = { ...packet }; | ||
} | ||
const player = this.manager.players.get(packet.guildId); | ||
if (packet.guildId && player) player.emit(packet.op, packet); | ||
packet.node = this; | ||
this.manager.emit( | ||
"debug", | ||
this.name, | ||
`[Web Socket] Lavalink Node Update : ${packet.op} ` | ||
); | ||
this.manager.emit("raw", packet); | ||
} | ||
#close(event) { | ||
this.disconnect(); | ||
this.manager.emit("nodeDisconnect",this,event); | ||
this.manager.emit("debug",this.name,`[Web Socket] Connection with Lavalink closed with Error code : ${event||"Unknown code"}`) | ||
if (event !== 1000){ | ||
} | ||
#close(event) { | ||
this.disconnect(); | ||
this.manager.emit("nodeDisconnect", this, event); | ||
this.manager.emit( | ||
"debug", | ||
this.name, | ||
`[Web Socket] Connection with Lavalink closed with Error code : ${ | ||
event || "Unknown code" | ||
}` | ||
); | ||
if (event !== 1000) { | ||
} | ||
} | ||
#error(event) { | ||
if (!event) return "Unknown event"; | ||
#error(event) { | ||
if (!event) return "Unknown event"; | ||
this.manager.emit( | ||
"debug", | ||
this.name, | ||
`[Web Socket] Connection for Lavalink node has error code: ${event.code}` | ||
); | ||
this.manager.emit("nodeError", this, event); | ||
return this.reconnect(); | ||
} | ||
this.manager.emit("debug",this.name,`[Web Socket] Connection for Lavalink node has error code: ${event.code}`) | ||
this.manager.emit("nodeError", this, event); | ||
return this.reconnect(); | ||
} | ||
destroy() { | ||
if (!this.isConnected) return; | ||
destroy(){ | ||
if(!this.isConnected) return; | ||
const players = this.manager.players.filter((p) => p.node == this); | ||
if (players.size) players.forEach((p) => p.destroy()); | ||
this.ws.close(1000, "destroy"); | ||
this.ws.removeAllListeners(); | ||
this.ws = null; | ||
this.reconnect = 1; | ||
this.destroyed = true; | ||
this.manager.nodes.delete(this.host); | ||
this.manager.emit("nodeDestroy", this); | ||
} | ||
const players = this.manager.players.filter(p => p.node == this); | ||
if (players.size) players.forEach(p => p.destroy()); | ||
this.ws.close(1000, "destroy"); | ||
this.ws.removeAllListeners(); | ||
this.ws = null; | ||
this.reconnect = 1; | ||
this.destroyed = true; | ||
this.manager.nodes.delete(this.host) | ||
this.manager.emit("nodeDestroy", this); | ||
reconnect() { | ||
this.reconnectAttempt = setTimeout(() => { | ||
this.isConnected = false; | ||
this.ws.removeAllListeners(); | ||
this.ws = null; | ||
this.manager.emit("nodeReconnect", this); | ||
this.connect(); | ||
}, this.reconnectTime); | ||
} | ||
send(payload) { | ||
const data = JSON.stringify(payload); | ||
this.ws.send(data, (error) => { | ||
if (error) return error; | ||
return null; | ||
}); | ||
this.manager.emit("raw", data, this.name); | ||
} | ||
get penalties() { | ||
let penalties = 0; | ||
if (!this.isConnected) return penalties; | ||
penalties += this.stats.players; | ||
penalties += Math.round( | ||
Math.pow(1.05, 100 * this.stats.cpu.systemLoad) * 10 - 10 | ||
); | ||
if (this.stats.frameStats) { | ||
penalties += this.stats.frameStats.deficit; | ||
penalties += this.stats.frameStats.nulled * 2; | ||
} | ||
reconnect() { | ||
this.reconnectAttempt = setTimeout(() => { | ||
this.isConnected = false; | ||
this.ws.removeAllListeners(); | ||
this.ws = null; | ||
this.manager.emit("nodeReconnect", this); | ||
this.connect(); | ||
}, this.reconnectTime); | ||
} | ||
send(payload) { | ||
const data = JSON.stringify(payload); | ||
this.ws.send(data, (error) => { | ||
if (error) return error; | ||
return null; | ||
}); | ||
this.manager.emit("raw", data, this.name) | ||
} | ||
get penalties(){ | ||
let penalties = 0; | ||
if (!this.isConnected) return penalties; | ||
penalties += this.stats.players; | ||
penalties += Math.round(Math.pow(1.05, 100 * this.stats.cpu.systemLoad) * 10 - 10); | ||
if (this.stats.frameStats) { | ||
penalties += this.stats.frameStats.deficit; | ||
penalties += this.stats.frameStats.nulled * 2; | ||
} | ||
return penalties; | ||
} | ||
return penalties; | ||
} | ||
} | ||
module.exports = Node; | ||
@@ -1,4 +0,5 @@ | ||
const {fetch} = require("undici") | ||
const PoruTrack = require("../guild/PoruTrack") | ||
let baseURL = /(?:https:\/\/music\.apple\.com\/)(?:.+)?(artist|album|music-video|playlist)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)/; | ||
const { fetch } = require("undici"); | ||
const PoruTrack = require("../guild/PoruTrack"); | ||
let baseURL = | ||
/(?:https:\/\/music\.apple\.com\/)(?:.+)?(artist|album|music-video|playlist)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)/; | ||
@@ -15,6 +16,5 @@ class AppleMusic { | ||
imageWeight: options.imageWeight || 500, | ||
} | ||
this.url = `https://amp-api.music.apple.com/v1/catalog/${this.options.searchMarket}` | ||
}; | ||
this.url = `https://amp-api.music.apple.com/v1/catalog/${this.options.searchMarket}`; | ||
this.token = null; | ||
} | ||
@@ -26,14 +26,15 @@ | ||
async requestToken() { | ||
try { | ||
let req = await fetch('https://music.apple.com/us/browse'); | ||
let req = await fetch("https://music.apple.com/us/browse"); | ||
let json = await req.text(); | ||
let config = /<meta name="desktop-music-app\/config\/environment" content="(.*?)">/.exec(json); | ||
let config = | ||
/<meta name="desktop-music-app\/config\/environment" content="(.*?)">/.exec( | ||
json | ||
); | ||
let key = config = JSON.parse(decodeURIComponent(config[1])); | ||
let { token } = key?.MEDIA_API | ||
let key = (config = JSON.parse(decodeURIComponent(config[1]))); | ||
let { token } = key?.MEDIA_API; | ||
if (!token) throw new Error("No acess key found for apple music") | ||
if (!token) throw new Error("No acess key found for apple music"); | ||
@@ -48,3 +49,2 @@ this.token = `Bearer ${token}`; | ||
async requestData(param) { | ||
@@ -56,5 +56,5 @@ if (!this.token) await this.requestToken(); | ||
Authorization: `${this.token}`, | ||
origin: 'https://music.apple.com' | ||
} | ||
}) | ||
origin: "https://music.apple.com", | ||
}, | ||
}); | ||
@@ -66,8 +66,4 @@ let body = await req.json(); | ||
async resolve(url) { | ||
let [, type, id] = await baseURL.exec(url) | ||
let [, type, id] = await baseURL.exec(url); | ||
@@ -91,19 +87,15 @@ switch (type) { | ||
try { | ||
let tracks = await this.requestData(`/search?types=songs&term=${query}`); | ||
let tracks = await this.requestData(`/search?types=songs&term=${query}`) | ||
let track = await this.buildUnresolved(tracks.results.songs.data[0]); | ||
let track = await this.buildUnresolved(tracks.results.songs.data[0]) | ||
return this.buildResponse('TRACK_LOADED', [track]); | ||
return this.buildResponse("TRACK_LOADED", [track]); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
} | ||
} | ||
@@ -113,19 +105,24 @@ | ||
try { | ||
let query = new URL(url).pathname.split('/'); | ||
let query = new URL(url).pathname.split("/"); | ||
let id = query.pop(); | ||
let playlist = await this.requestData(`/playlists/${id}`) | ||
let name = playlist.data[0].attributes.name | ||
let playlist = await this.requestData(`/playlists/${id}`); | ||
let name = playlist.data[0].attributes.name; | ||
const limitedTracks = this.options.playlistLimit | ||
? playlist.data[0].relationships.tracks.data.slice(0, this.options.playlistLimit * 100) | ||
? playlist.data[0].relationships.tracks.data.slice( | ||
0, | ||
this.options.playlistLimit * 100 | ||
) | ||
: playlist.data[0].relationships.tracks.data; | ||
let tracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))) | ||
return this.buildResponse('PLAYLIST_LOADED', tracks, name); | ||
let tracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
return this.buildResponse("PLAYLIST_LOADED", tracks, name); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
@@ -135,28 +132,27 @@ } | ||
async fetchAlbum(url) { | ||
try { | ||
let query = new URL(url).pathname.split('/'); | ||
let query = new URL(url).pathname.split("/"); | ||
let id = query.pop(); | ||
let album = await this.requestData(`/albums/${id}`) | ||
let album = await this.requestData(`/albums/${id}`); | ||
const limitedTracks = this.options.albumLimit | ||
? album.data[0].relationships.tracks.data.slice(0, this.options.albumLimit * 100) | ||
? album.data[0].relationships.tracks.data.slice( | ||
0, | ||
this.options.albumLimit * 100 | ||
) | ||
: album.data[0].relationships.tracks.data; | ||
let name = album.data[0].attributes.name | ||
let tracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
return this.buildResponse('PLAYLIST_LOADED', tracks, name); | ||
let name = album.data[0].attributes.name; | ||
let tracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
return this.buildResponse("PLAYLIST_LOADED", tracks, name); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
} | ||
@@ -166,42 +162,42 @@ } | ||
async fetchArtist(url) { | ||
try { | ||
let query = new URL(url).pathname.split('/'); | ||
let query = new URL(url).pathname.split("/"); | ||
let id = query.pop(); | ||
let artist = await this.requestData(`/attists/${id}`) | ||
let name = artistdata[0].attributes.name | ||
let artist = await this.requestData(`/attists/${id}`); | ||
let name = artist.data[0].attributes.name; | ||
const limitedTracks = this.options.artistLimit | ||
? artist.data[0].relationships.tracks.data.slice(0, this.options.artist * 100) | ||
? artist.data[0].relationships.tracks.data.slice( | ||
0, | ||
this.options.artist * 100 | ||
) | ||
: artist.data[0].relationships.tracks.data; | ||
let tracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
return this.buildResponse('PLAYLIST_LOADED', tracks, name); | ||
let tracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
return this.buildResponse("PLAYLIST_LOADED", tracks, name); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
} | ||
} | ||
async buildUnresolved(track) { | ||
if (!track) throw new ReferenceError('The Apple track object was not provided'); | ||
if (!track) | ||
throw new ReferenceError("The Apple track object was not provided"); | ||
return new PoruTrack({ | ||
track: '', | ||
track: "", | ||
info: { | ||
sourceName: 'Apple Music', | ||
sourceName: "Apple Music", | ||
identifier: track.id, | ||
isSeekable: true, | ||
author: track.attributes.artistName ? track.attributes.artistName : 'Unknown', | ||
author: track.attributes.artistName | ||
? track.attributes.artistName | ||
: "Unknown", | ||
length: track.attributes.durationInMillis, | ||
@@ -211,3 +207,5 @@ isStream: false, | ||
uri: track.attributes.url, | ||
image: track.attributes.artwork.url.replace("{w}", this.options.imageWeight).replace("{h}", this.options.imageHeight) | ||
image: track.attributes.artwork.url | ||
.replace("{w}", this.options.imageWeight) | ||
.replace("{h}", this.options.imageHeight), | ||
}, | ||
@@ -218,3 +216,5 @@ }); | ||
compareValue(value) { | ||
return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined'; | ||
return typeof value !== "undefined" | ||
? value !== null | ||
: typeof value !== "undefined"; | ||
} | ||
@@ -229,30 +229,8 @@ | ||
}, | ||
exceptionMsg ? { exception: { message: exceptionMsg, severity: 'COMMON' } } : {}, | ||
exceptionMsg | ||
? { exception: { message: exceptionMsg, severity: "COMMON" } } | ||
: {} | ||
); | ||
} | ||
} | ||
module.exports = AppleMusic | ||
const apple = new AppleMusic("",{apple:{}}) | ||
apple.resolve("https://music.apple.com/us/playlist/bollywood-hits/pl.d60caf02fcce4d7e9788fe01243b7c2c") | ||
module.exports = AppleMusic; |
@@ -1,22 +0,19 @@ | ||
const {fetch} = require("undici") | ||
const { fetch } = require("undici"); | ||
let REGEX = /^(?:https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?(track|album|playlist|artist)\/(\d+)/ | ||
let REGEX = | ||
/^(?:https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?(track|album|playlist|artist)\/(\d+)/; | ||
const PoruTrack = require("../guild/PoruTrack") | ||
const PoruTrack = require("../guild/PoruTrack"); | ||
class Deezer { | ||
constructor(manager, options) { | ||
this.manager = manager; | ||
this.baseURL = 'https://api.deezer.com'; | ||
this.baseURL = "https://api.deezer.com"; | ||
this.options = { | ||
playlistLimit: options.playlistLimit || null, | ||
albumLimit: options.albumLimit || null, | ||
artistLimit: options.artistLimit || null | ||
} | ||
artistLimit: options.artistLimit || null, | ||
}; | ||
} | ||
check(url) { | ||
@@ -27,4 +24,3 @@ return REGEX.test(url); | ||
async requestData(endpoint) { | ||
const req = await fetch(`${this.baseURL}/${endpoint}`, { | ||
}); | ||
const req = await fetch(`${this.baseURL}/${endpoint}`, {}); | ||
const data = await req.json(); | ||
@@ -34,26 +30,20 @@ return data; | ||
async resolve(url) { | ||
const [, type, id] = REGEX.exec(url) ?? []; | ||
switch (type) { | ||
case 'playlist': { | ||
case "playlist": { | ||
return this.fetchPlaylist(id); | ||
} | ||
case 'track': { | ||
case "track": { | ||
return this.fetchTrack(id); | ||
} | ||
case 'album': { | ||
case "album": { | ||
return this.fetchAlbum(id); | ||
} | ||
case 'artist': { | ||
case "artist": { | ||
return this.fetchArtist(id); | ||
} | ||
} | ||
} | ||
async fetchPlaylist(id) { | ||
@@ -64,15 +54,19 @@ try { | ||
const limitedTracks = this.options.playlistLimit | ||
? playlist.track.data.slice(0, this.options.playlistLimit * 100) | ||
: playlist.track.data; | ||
? playlist.tracks.data.slice(0, this.options.playlistLimit * 100) | ||
: playlist.tracks.data; | ||
const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, playlist.name); | ||
const unresolvedPlaylistTracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedPlaylistTracks, | ||
playlist.title | ||
); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
@@ -90,15 +84,18 @@ } | ||
const unresolvedAlbumTracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
const unresolvedAlbumTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedAlbumTracks, album.name); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedAlbumTracks, | ||
album.name | ||
); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
} | ||
@@ -108,17 +105,14 @@ } | ||
async fetchTrack(id) { | ||
try { | ||
const track = await this.requestData(`/track/${id}`) | ||
const track = await this.requestData(`/track/${id}`); | ||
const unresolvedTrack = await Promise.all(this.buildUnresolved(track)); | ||
return this.buildResponse('TRACK_LOADED', [unresolvedTrack]); | ||
return this.buildResponse("TRACK_LOADED", [unresolvedTrack]); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
} | ||
@@ -128,6 +122,5 @@ } | ||
async fetchArtist(id) { | ||
try { | ||
const artist = await this.requestData(`/artist/${id}/top`); | ||
await this.fetchArtistTracks(artist) | ||
await this.fetchArtistTracks(artist); | ||
@@ -138,17 +131,18 @@ const limitedTracks = this.options.artistLimit | ||
const unresolvedArtistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x) | ||
)); | ||
const unresolvedArtistTracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedArtistTracks, artist.name); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedArtistTracks, | ||
artist.name | ||
); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
} | ||
@@ -162,4 +156,4 @@ } | ||
if (!nextPage) break; | ||
const req = await fetch(nextPage) | ||
const json = await req.json() | ||
const req = await fetch(nextPage); | ||
const json = await req.json(); | ||
@@ -177,30 +171,28 @@ deezerArtist.data.push(...json.data); | ||
try { | ||
if (this.check(query)) return this.resolve(query) | ||
let tracks = await this.requestData(`/search?q="${query}"`) | ||
if (this.check(query)) return this.resolve(query); | ||
let tracks = await this.requestData(`/search?q="${query}"`); | ||
const unresolvedTrack = await this.buildUnresolved(tracks.data[0]); | ||
return this.buildResponse('TRACK_LOADED', [unresolvedTrack]); | ||
return this.buildResponse("TRACK_LOADED", [unresolvedTrack]); | ||
} catch (e) { | ||
return this.buildResponse( | ||
'LOAD_FAILED', | ||
"LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
} | ||
} | ||
async buildUnresolved(track) { | ||
if (!track) throw new ReferenceError('The Deezer track object was not provided'); | ||
if (!track) | ||
throw new ReferenceError("The Deezer track object was not provided"); | ||
return new PoruTrack({ | ||
track: '', | ||
track: "", | ||
info: { | ||
sourceName: 'deezer', | ||
sourceName: "deezer", | ||
identifier: track.id, | ||
isSeekable: true, | ||
author: track.artist ? track.artist.name : 'Unknown', | ||
author: track.artist ? track.artist.name : "Unknown", | ||
length: track.duration, | ||
@@ -210,3 +202,3 @@ isStream: false, | ||
uri: track.link, | ||
image: track.album.cover_medium | ||
image: track.album.cover_medium, | ||
}, | ||
@@ -216,5 +208,6 @@ }); | ||
compareValue(value) { | ||
return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined'; | ||
return typeof value !== "undefined" | ||
? value !== null | ||
: typeof value !== "undefined"; | ||
} | ||
@@ -229,16 +222,9 @@ | ||
}, | ||
exceptionMsg ? { exception: { message: exceptionMsg, severity: 'COMMON' } } : {}, | ||
exceptionMsg | ||
? { exception: { message: exceptionMsg, severity: "COMMON" } } | ||
: {} | ||
); | ||
} | ||
} | ||
module.exports = Deezer; | ||
let deezer = new Deezer("", { deezer: { playlistLimit: 10 } }) | ||
deezer.resolve("https://www.deezer.com/en/playlist/4404579662") |
@@ -1,7 +0,7 @@ | ||
const {fetch} = require("undici") | ||
const { fetch } = require("undici"); | ||
let spotifyPattern = | ||
/^(?:https:\/\/open\.spotify\.com\/(?:user\/[A-Za-z0-9]+\/)?|spotify:)(album|playlist|track|artist)(?:[/:])([A-Za-z0-9]+).*$/; | ||
/^(?:https:\/\/open\.spotify\.com\/(?:user\/[A-Za-z0-9]+\/)?|spotify:)(album|playlist|track|artist)(?:[/:])([A-Za-z0-9]+).*$/; | ||
const PoruTrack = require("../guild/PoruTrack") | ||
const PoruTrack = require("../guild/PoruTrack"); | ||
@@ -11,11 +11,12 @@ class Spotify { | ||
this.manager = manager; | ||
this.baseURL = 'https://api.spotify.com/v1'; | ||
this.options ={ | ||
playlistLimit : manager.options.playlistLimit, | ||
albumLimit : manager.options.albumLimit, | ||
artistLimit : manager.options.artistLimit, | ||
searchMarket : manager.options.searchMarket | ||
} | ||
this.authorization = Buffer.from(`${this.options.clientID}:${this.options.clientSecret}`).toString('base64'); | ||
this.baseURL = "https://api.spotify.com/v1"; | ||
this.options = { | ||
playlistLimit: manager.options.playlistLimit, | ||
albumLimit: manager.options.albumLimit, | ||
artistLimit: manager.options.artistLimit, | ||
searchMarket: manager.options.searchMarket, | ||
}; | ||
this.authorization = Buffer.from( | ||
`${this.options.clientID}:${this.options.clientSecret}` | ||
).toString("base64"); | ||
this.interval = 0; | ||
@@ -25,28 +26,28 @@ } | ||
check(url) { | ||
return spotifyPattern.test(url); | ||
} | ||
async requestToken() { | ||
try { | ||
const data = await fetch( | ||
"https://open.spotify.com/get_access_token?reason=transport&productType=embed", | ||
{ | ||
headers: { | ||
"User-Agent": | ||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36", | ||
}, | ||
} | ||
); | ||
async requestToken() { | ||
try{ | ||
const data = await fetch('https://open.spotify.com/get_access_token?reason=transport&productType=embed', { | ||
headers: { | ||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36' | ||
const body = await data.json(); | ||
this.token = `Bearer ${body.accessToken}`; | ||
this.interval = body.accessTokenExpirationTimestampMs * 1000; | ||
} catch (e) { | ||
if (e.status === 400) { | ||
throw new Error("Invalid Spotify client."); | ||
} | ||
}) | ||
const body = await data.json(); | ||
this.token = `Bearer ${body.accessToken}`; | ||
this.interval = body.accessTokenExpirationTimestampMs * 1000; | ||
} catch (e) { | ||
if (e.status === 400) { | ||
throw new Error('Invalid Spotify client.'); | ||
} | ||
} | ||
} | ||
/* | ||
/* | ||
async requestToken() { | ||
@@ -83,5 +84,8 @@ | ||
const req = await fetch(`${this.baseURL}${/^\//.test(endpoint) ? endpoint : `/${endpoint}`}`, { | ||
headers: { Authorization: this.token }, | ||
}); | ||
const req = await fetch( | ||
`${this.baseURL}${/^\//.test(endpoint) ? endpoint : `/${endpoint}`}`, | ||
{ | ||
headers: { Authorization: this.token }, | ||
} | ||
); | ||
const data = await req.json(); | ||
@@ -92,19 +96,18 @@ return data; | ||
async resolve(url) { | ||
if (!this.token) await this.requestToken(); | ||
if (!this.token) await this.requestToken(); | ||
const [, type, id] = spotifyPattern.exec(url) ?? []; | ||
switch (type) { | ||
case 'playlist': { | ||
case "playlist": { | ||
return this.fetchPlaylist(id); | ||
} | ||
case 'track': { | ||
case "track": { | ||
return this.fetchTrack(id); | ||
} | ||
case 'album': { | ||
case "album": { | ||
return this.fetchAlbum(id); | ||
} | ||
case 'artist': { | ||
case "artist": { | ||
return this.fetchArtist(id); | ||
} | ||
} | ||
@@ -122,11 +125,17 @@ } | ||
const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x.track))); | ||
const unresolvedPlaylistTracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x.track)) | ||
); | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, playlist.name); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedPlaylistTracks, | ||
playlist.name | ||
); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.status === 404 ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
e.status === 404 ? "NO_MATCHES" : "LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
@@ -140,12 +149,20 @@ } | ||
const limitedTracks = this.options.albumLimit ? album.tracks.items.slice(0, this.options.albumLimit * 100) : album.tracks.items; | ||
const limitedTracks = this.options.albumLimit | ||
? album.tracks.items.slice(0, this.options.albumLimit * 100) | ||
: album.tracks.items; | ||
const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, album.name); | ||
const unresolvedPlaylistTracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedPlaylistTracks, | ||
album.name | ||
); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
@@ -159,15 +176,25 @@ } | ||
const data = await this.requestData(`/artists/${id}/top-tracks?market=${this.searchMarket ?? 'US'}`); | ||
const data = await this.requestData( | ||
`/artists/${id}/top-tracks?market=${this.searchMarket ?? "US"}` | ||
); | ||
const limitedTracks = this.options.artistLimit ? data.tracks.slice(0, this.options.artistLimit * 100) : data.tracks; | ||
const limitedTracks = this.options.artistLimit | ||
? data.tracks.slice(0, this.options.artistLimit * 100) | ||
: data.tracks; | ||
const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
const unresolvedPlaylistTracks = await Promise.all( | ||
limitedTracks.map((x) => this.buildUnresolved(x)) | ||
); | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, artist.name); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedPlaylistTracks, | ||
artist.name | ||
); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
@@ -181,9 +208,9 @@ } | ||
const unresolvedTrack = await this.buildUnresolved(data); | ||
return this.buildResponse('TRACK_LOADED', [unresolvedTrack]); | ||
return this.buildResponse("TRACK_LOADED", [unresolvedTrack]); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
@@ -198,3 +225,5 @@ } | ||
const data = await this.requestData( | ||
`/search/?q="${query}"&type=artist,album,track&market=${this.options.searchMarket ?? 'US'}`, | ||
`/search/?q="${query}"&type=artist,album,track&market=${ | ||
this.options.searchMarket ?? "US" | ||
}` | ||
); | ||
@@ -204,9 +233,9 @@ | ||
return this.buildResponse('TRACK_LOADED', [unresolvedTrack]); | ||
return this.buildResponse("TRACK_LOADED", [unresolvedTrack]); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
e.body?.error.message ?? e.message | ||
); | ||
@@ -234,11 +263,12 @@ } | ||
async buildUnresolved(track) { | ||
if (!track) throw new ReferenceError('The Spotify track object was not provided'); | ||
if (!track) | ||
throw new ReferenceError("The Spotify track object was not provided"); | ||
return new PoruTrack({ | ||
track: '', | ||
track: "", | ||
info: { | ||
sourceName: 'spotify', | ||
sourceName: "spotify", | ||
identifier: track.id, | ||
isSeekable: true, | ||
author: track.artists[0] ? track.artists[0].name : 'Unknown', | ||
author: track.artists[0] ? track.artists[0].name : "Unknown", | ||
length: track.duration_ms, | ||
@@ -254,3 +284,5 @@ isStream: false, | ||
async fetchMetaData(track) { | ||
const fetch = await this.manager.resolve(`${track.info.title} ${track.info.author}`); | ||
const fetch = await this.manager.resolve( | ||
`${track.info.title} ${track.info.author}` | ||
); | ||
return fetch.tracks[0]; | ||
@@ -269,3 +301,5 @@ } | ||
compareValue(value) { | ||
return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined'; | ||
return typeof value !== "undefined" | ||
? value !== null | ||
: typeof value !== "undefined"; | ||
} | ||
@@ -280,3 +314,5 @@ | ||
}, | ||
exceptionMsg ? { exception: { message: exceptionMsg, severity: 'COMMON' } } : {}, | ||
exceptionMsg | ||
? { exception: { message: exceptionMsg, severity: "COMMON" } } | ||
: {} | ||
); | ||
@@ -283,0 +319,0 @@ } |
const { EventEmitter } = require("events"); | ||
const Queue = require("./guild/Queue"); | ||
const Filters = require("./guild/Filter") | ||
const Filters = require("./guild/Filter"); | ||
class Player extends EventEmitter { | ||
constructor(manager, node, options) { | ||
super(); | ||
constructor(manager, node, options) { | ||
super(); | ||
this.manager = manager; | ||
this.manager = manager; | ||
this.queue = new Queue(); | ||
this.queue = new Queue(); | ||
this.node = node; | ||
this.node = node; | ||
this.options = options; | ||
this.options = options; | ||
this.filters = new Filters(this, this.node) | ||
this.filters = new Filters(this, this.node); | ||
this.guildId = options.guildId; | ||
this.guildId = options.guildId; | ||
this.voiceChannel = options.voiceChannel.id || options.voiceChannel; | ||
this.voiceChannel = options.voiceChannel.id || options.voiceChannel; | ||
this.textChannel = options.textChannel || null; | ||
this.textChannel = options.textChannel || null; | ||
this.shardId = options.shardId || 1; | ||
this.shardId = options.shardId || 1; | ||
this.isConnected = false; | ||
this.isConnected = false; | ||
this.isPlaying = false; | ||
this.isPlaying = false; | ||
this.isPaused = false; | ||
this.isPaused = false; | ||
this.trackRepeat = false; | ||
this.trackRepeat = false; | ||
this.queueRepeat = false; | ||
this.queueRepeat = false; | ||
this.loop = 0; | ||
this.loop = 0; | ||
this.position = 0; | ||
this.position = 0; | ||
this.volume = 100; | ||
this.volume = 100; | ||
this.currentTrack = {}; | ||
this.currentTrack = {}; | ||
this.previousTrack = null; | ||
this.previousTrack = null; | ||
this.voiceUpdateState = null; | ||
this.voiceUpdateState = null; | ||
this.on("event", (data) => this.lavalinkEvent(data).bind(this)()); | ||
this.on("playerUpdate", (packet) => { | ||
(this.isConnected = packet.state.connected), | ||
(this.position = packet.state.position); | ||
this.manager.emit("playerUpdate", this, packet); | ||
}); | ||
} | ||
this.on("event", (data) => (this.lavalinkEvent(data).bind(this))()); | ||
this.on("playerUpdate", (packet) => { | ||
this.isConnected = packet.state.connected, | ||
this.position = packet.state.position | ||
this.manager.emit("playerUpdate", this, packet); | ||
}); | ||
async play(options = {}) { | ||
if (!this.queue.length) { | ||
return null; | ||
} | ||
async play(options = {}) { | ||
this.currentTrack = this.queue.shift(); | ||
if (!this.queue.length) { | ||
return null; | ||
} | ||
this.currentTrack = this.queue.shift() | ||
if (!this.currentTrack.track) { | ||
this.currentTrack = await this.currentTrack.resolve(this.manager); | ||
} | ||
this.isPlaying = true; | ||
this.node.send({ | ||
op: "play", | ||
guildId: this.guildId, | ||
track: this.currentTrack.track, | ||
noReplace: options.noReplace || true, | ||
}); | ||
this.position = 0; | ||
return this; | ||
if (!this.currentTrack.track) { | ||
this.currentTrack = await this.currentTrack.resolve(this.manager); | ||
} | ||
this.isPlaying = true; | ||
this.node.send({ | ||
op: "play", | ||
guildId: this.guildId, | ||
track: this.currentTrack.track, | ||
noReplace: options.noReplace || true, | ||
}); | ||
this.position = 0; | ||
return this; | ||
} | ||
stop() { | ||
stop() { | ||
this.position = 0; | ||
this.isConnected = false; | ||
this.isPlaying = false; | ||
this.node.send({ | ||
op: "stop", | ||
guildId: this.guildId, | ||
}); | ||
return this; | ||
} | ||
this.position = 0; | ||
this.isConnected = false | ||
this.isPlaying = false; | ||
this.node.send({ | ||
op: "stop", | ||
guildId: this.guildId | ||
}); | ||
return this; | ||
} | ||
pause(pause = true) { | ||
if (typeof pause !== "boolean") | ||
throw new RangeError("Pause function must be pass with boolean value."); | ||
pause(pause = true) { | ||
if (typeof pause !== "boolean") throw new RangeError("Pause function must be pass with boolean value."); | ||
this.node.send({ | ||
op: "pause", | ||
guildId: this.guildId, | ||
pause, | ||
}); | ||
this.isPlaying = !pause; | ||
this.isPaused = pause; | ||
this.node.send({ | ||
op: "pause", | ||
guildId: this.guildId, | ||
pause, | ||
}); | ||
this.isPlaying = !pause; | ||
this.isPaused = pause; | ||
return this; | ||
} | ||
return this; | ||
} | ||
async seekTo(position) { | ||
if (Number.isNaN(position)) | ||
throw new RangeError("[Poru Error] Position must be a number."); | ||
this.position = position; | ||
this.node.send({ | ||
op: "seek", | ||
guildId: this.guildId, | ||
position, | ||
}); | ||
return this; | ||
} | ||
async seekTo(position) { | ||
if (Number.isNaN(position)) throw new RangeError("[Poru Error] Position must be a number."); | ||
this.position = position; | ||
this.node.send({ | ||
op: "seek", | ||
guildId: this.guildId, | ||
position, | ||
}); | ||
return this; | ||
} | ||
setVolume(volume) { | ||
if (Number.isNaN(volume)) | ||
throw new RangeError("Volume level must be a number."); | ||
this.volume = volume; | ||
this.node.send({ | ||
op: "volume", | ||
guildId: this.guildId, | ||
volume: this.volume, | ||
}); | ||
return this; | ||
} | ||
setVolume(volume) { | ||
if (Number.isNaN(volume)) throw new RangeError("Volume level must be a number."); | ||
this.volume = volume; | ||
this.node.send({ | ||
op: "volume", | ||
guildId: this.guildId, | ||
volume: this.volume, | ||
}); | ||
return this; | ||
TrackRepeat() { | ||
this.loop = 1; | ||
this.trackRepeat = true; | ||
this.queueRepeat = false; | ||
return this; | ||
} | ||
} | ||
QueueRepeat() { | ||
this.loop = 2; | ||
this.queueRepeat = true; | ||
this.trackRepeat = false; | ||
return this; | ||
} | ||
TrackRepeat() { | ||
this.loop = 1; | ||
this.trackRepeat = true; | ||
this.queueRepeat = false; | ||
return this; | ||
} | ||
DisableRepeat() { | ||
this.loop = 0; | ||
this.trackRepeat = false; | ||
this.queueRepeat = false; | ||
return this; | ||
} | ||
setTextChannel(channel) { | ||
if (typeof channel !== "string") | ||
throw new RangeError("Channel must be a string."); | ||
this.textChannel = channel; | ||
return this; | ||
} | ||
QueueRepeat() { | ||
this.loop = 2; | ||
this.queueRepeat = true; | ||
this.trackRepeat = false; | ||
return this; | ||
} | ||
setVoiceChannel(channel) { | ||
if (typeof channel !== "string") | ||
throw new RangeError("Channel must be a string."); | ||
this.voiceChannel = channel; | ||
return this; | ||
} | ||
DisableRepeat() { | ||
this.loop = 0; | ||
this.trackRepeat = false; | ||
this.queueRepeat = false; | ||
return this; | ||
} | ||
connect(options) { | ||
let { guildId, voiceChannel, deaf, mute } = options; | ||
this.send( | ||
{ | ||
guild_id: guildId, | ||
channel_id: voiceChannel, | ||
self_deaf: deaf || true, | ||
self_mute: mute || false, | ||
}, | ||
true | ||
); | ||
this.isConnected = true; | ||
} | ||
setTextChannel(channel) { | ||
if (typeof channel !== "string") throw new RangeError("Channel must be a string."); | ||
this.textChannel = channel; | ||
return this; | ||
updateSession(data) { | ||
if (data) { | ||
this.voiceUpdateState = data; | ||
this.node.send({ | ||
op: "voiceUpdate", | ||
guildId: this.guildId, | ||
...data, | ||
}); | ||
} | ||
return this; | ||
} | ||
setVoiceChannel(channel) { | ||
if (typeof channel !== "string") throw new RangeError("Channel must be a string."); | ||
this.voiceChannel = channel; | ||
return this; | ||
} | ||
reconnect() { | ||
if (this.voiceChannel === null) return null; | ||
this.send({ | ||
guild_id: this.guildId, | ||
channel_id: this.voiceChannel, | ||
self_mute: false, | ||
self_deaf: false, | ||
}); | ||
connect(options) { | ||
return this; | ||
} | ||
let { guildId, voiceChannel, deaf, mute } = options; | ||
this.send({ guild_id: guildId, channel_id: voiceChannel, self_deaf: deaf || true, self_mute: mute || false }, true); | ||
this.isConnected = true; | ||
disconnect() { | ||
if (this.voiceChannel === null) return null; | ||
this.pause(true); | ||
this.isConnected = false; | ||
this.send({ | ||
guild_id: this.guildId, | ||
channel_id: null, | ||
self_mute: false, | ||
self_deaf: false, | ||
}); | ||
this.voiceChannel = null; | ||
return this; | ||
} | ||
} | ||
destroy() { | ||
this.disconnect(); | ||
this.node.send({ | ||
op: "destroy", | ||
guildId: this.guildId, | ||
}); | ||
this.manager.emit("playerDestroy", this); | ||
this.manager.players.delete(this.guildId); | ||
} | ||
updateSession(data) { | ||
if (data) { | ||
this.voiceUpdateState = data; | ||
this.node.send({ | ||
op: "voiceUpdate", | ||
guildId: this.guildId, | ||
...data, | ||
}); | ||
} | ||
return this; | ||
restart() { | ||
this.filters.updateFilters(); | ||
if (this.currentTrack) { | ||
this.isPlaying = true; | ||
this.node.send({ | ||
op: "play", | ||
startTime: this.position, | ||
noReplace: true, | ||
guildId: this.guildId, | ||
track: this.currentTrack.track, | ||
pause: this.isPaused, | ||
}); | ||
} | ||
} | ||
reconnect() { | ||
if (this.voiceChannel === null) return null; | ||
this.send({ | ||
guild_id: this.guildId, | ||
channel_id: this.voiceChannel, | ||
self_mute: false, | ||
self_deaf: false, | ||
}) | ||
return this; | ||
} | ||
async autoplay(option = false) { | ||
if (!option) return false; | ||
try { | ||
let data = `https://www.youtube.com/watch?v=${ | ||
this.previousTrack.info.identifier || this.currentTrack.info.identifier | ||
}&list=RD${ | ||
this.previousTrack.info.identifier || this.currentTrack.info.identifier | ||
}`; | ||
disconnect() { | ||
if (this.voiceChannel === null) return null; | ||
this.pause(true); | ||
this.isConnected = false; | ||
this.send({ | ||
guild_id: this.guildId, | ||
channel_id: null, | ||
self_mute: false, | ||
self_deaf: false, | ||
}); | ||
this.voiceChannel = null; | ||
return this; | ||
} | ||
let response = await this.manager.resolve( | ||
data, | ||
this.manager.options.defaultPlatform || "ytsearch" | ||
); | ||
destroy() { | ||
this.disconnect(); | ||
this.node.send({ | ||
op: "destroy", | ||
guildId: this.guildId, | ||
}); | ||
this.manager.emit("playerDestroy", this); | ||
this.manager.players.delete(this.guildId); | ||
} | ||
if ( | ||
!response || | ||
!response.tracks || | ||
["LOAD_FAILED", "NO_MATCHES"].includes(response.loadType) | ||
) | ||
return this.stop(); | ||
restart() { | ||
this.filters.updateFilters(); | ||
if (this.currentTrack) { | ||
let track = | ||
response.tracks[ | ||
Math.floor(Math.random() * Math.floor(response.tracks.length)) | ||
]; | ||
this.isPlaying = true; | ||
this.node.send({ | ||
op: "play", | ||
startTime: this.position, | ||
noReplace: true, | ||
guildId: this.guildId, | ||
track: this.currentTrack.track, | ||
pause: this.isPaused | ||
}); | ||
this.queue.push(track); | ||
this.play(); | ||
} | ||
return this; | ||
} catch (e) { | ||
console.log(`[Poru Autoplay] error : ${e}`); | ||
return this.stop(); | ||
} | ||
} | ||
async autoplay(toggle = false) { | ||
send(data) { | ||
this.manager.sendData({ op: 4, d: data }); | ||
} | ||
if (!toggle) return null; | ||
try { | ||
if (!this.previousTrack) return this.stop(); | ||
let data = `https://www.youtube.com/watch?v=${this.previousTrack.info.identifier}&list=RD${this.previousTrack.info.identifier}`; | ||
lavalinkEvent(data) { | ||
const events = { | ||
TrackStartEvent() { | ||
this.isPlaying = true; | ||
this.isPaused = false; | ||
this.manager.emit("trackStart", this, this.currentTrack, data); | ||
}, | ||
// eslint-disable-next-line consistent-return | ||
TrackEndEvent() { | ||
this.previousTrack = this.currentTrack; | ||
let response = await this.manager.resolve(data,this.manager.options.defaultPlatform || "ytsearch"); | ||
if (this.currentTrack && this.loop === 1) { | ||
this.queue.unshift(this.previousTrack); | ||
this.manager.emit("trackEnd", this, this.currentTrack, data); | ||
return this.play(); | ||
} else if (this.currentTrack && this.loop === 2) { | ||
this.queue.push(this.previousTrack); | ||
this.manager.emit("trackEnd", this, this.currentTrack, data); | ||
if (!response || !response.tracks || ["LOAD_FAILED", "NO_MATCHES"].includes(response.loadType)) return this.stop(); | ||
return this.play(); | ||
} | ||
let track = response.tracks[Math.floor(Math.random() * Math.floor(response.tracks.length))]; | ||
this.queue.push(track); | ||
this.play(); | ||
return this; | ||
} catch (e) { | ||
console.log(`[Poru Autoplay] error : ${e}`) | ||
return this.stop(); | ||
if (this.queue.length === 0) { | ||
return this.manager.emit("queueEnd", this, this.track, data); | ||
} else if (this.queue.length > 0) { | ||
this.manager.emit("trackEnd", this, this.currentTrack, data); | ||
return this.play(); | ||
} | ||
} | ||
send(data) { | ||
this.manager.sendData({ op: 4, d: data }); | ||
} | ||
lavalinkEvent(data) { | ||
const events = { | ||
TrackStartEvent() { | ||
this.isPlaying = true; | ||
this.isPaused = false; | ||
this.manager.emit("trackStart", this, this.currentTrack, data); | ||
}, | ||
// eslint-disable-next-line consistent-return | ||
TrackEndEvent() { | ||
this.previousTrack = this.currentTrack; | ||
if (this.currentTrack && this.loop === 1) { | ||
this.queue.unshift(this.previousTrack) | ||
this.manager.emit("trackEnd", this, this.currentTrack, data) | ||
return this.play(); | ||
} else if (this.currentTrack && this.loop === 2) { | ||
this.queue.push(this.previousTrack) | ||
this.manager.emit("trackEnd", this, this.currentTrack, data) | ||
return this.play(); | ||
} | ||
if (this.queue.length === 0) { | ||
this.manager.emit("queueEnd",this, this.track, data); | ||
return this.destroy(); | ||
} else if (this.queue.length > 0) { | ||
this.manager.emit("trackEnd", this, this.currentTrack, data) | ||
return this.play(); | ||
} | ||
this.manager.emit("queueEnd", this, this.currentTrack, data); | ||
this.destroy(); | ||
}, | ||
TrackStuckEvent() { | ||
this.manager.emit("trackError", this,this.currentTrack, data); | ||
this.stop(); | ||
}, | ||
TrackExceptionEvent() { | ||
this.manager.emit("trackError", this, this.track, data); | ||
this.stop(); | ||
}, | ||
WebSocketClosedEvent() { | ||
if ([4015, 4009].includes(data.code)) { | ||
this.send({ | ||
guild_id: data.guildId, | ||
channel_id: this.voiceChannel.id || this.voiceChannel, | ||
self_mute: this.options.mute || false, | ||
self_deaf: this.options.deaf || false, | ||
}); | ||
} | ||
this.manager.emit("socketClosed", this, data); | ||
}, | ||
default() { | ||
throw new Error(`An unknown event: ${data}`); | ||
}, | ||
}; | ||
return events[data.type] || events.default; | ||
} | ||
this.manager.emit("queueEnd", this, this.currentTrack, data); | ||
this.destroy(); | ||
}, | ||
TrackStuckEvent() { | ||
this.manager.emit("trackError", this, this.currentTrack, data); | ||
this.stop(); | ||
}, | ||
TrackExceptionEvent() { | ||
this.manager.emit("trackError", this, this.track, data); | ||
this.stop(); | ||
}, | ||
WebSocketClosedEvent() { | ||
if ([4015, 4009].includes(data.code)) { | ||
this.send({ | ||
guild_id: data.guildId, | ||
channel_id: this.voiceChannel.id || this.voiceChannel, | ||
self_mute: this.options.mute || false, | ||
self_deaf: this.options.deaf || false, | ||
}); | ||
} | ||
this.manager.emit("socketClosed", this, data); | ||
}, | ||
default() { | ||
throw new Error(`An unknown event: ${data}`); | ||
}, | ||
}; | ||
return events[data.type] || events.default; | ||
} | ||
} | ||
module.exports = Player; | ||
494
src/Poru.js
const { EventEmitter } = require("events"); | ||
const { fetch } = require("undici") | ||
const config = require("./config") | ||
const { fetch } = require("undici"); | ||
const config = require("./config"); | ||
const Player = require("./Player"); | ||
const Node = require("./Node"); | ||
const Response = require("./guild/Response"); | ||
const Spotify = require("./platform/Spotify") | ||
const AppleMusic = require("./platform/AppleMusic") | ||
const Deezer = require("./platform/Deezer") | ||
const Spotify = require("./platform/Spotify"); | ||
const AppleMusic = require("./platform/AppleMusic"); | ||
const Deezer = require("./platform/Deezer"); | ||
class Poru extends EventEmitter { | ||
constructor(client, nodes, options = {}) { | ||
super(); | ||
if (!client) throw new Error("[Poru Error] You didn't provide a valid client"); | ||
if (!nodes) throw new Error("[Poru Error] You didn't provide a lavalink node"); | ||
constructor(client, nodes, options = {}) { | ||
super(); | ||
if (!client) | ||
throw new Error("[Poru Error] You didn't provide a valid client"); | ||
if (!nodes) | ||
throw new Error("[Poru Error] You didn't provide a lavalink node"); | ||
this.client = client; | ||
this._nodes = nodes; | ||
this.nodes = new Map(); | ||
this.players = new Map(); | ||
this.voiceStates = new Map(); | ||
this.voiceServers = new Map(); | ||
this.isReady = false; | ||
this.user = null; | ||
this.options = options | ||
this.shards = options.shards || 1; | ||
this.sendData = null; | ||
this.version = config.version | ||
this.spotify = new Spotify(this, this.options); | ||
this.apple = new AppleMusic(this, this.options) | ||
this.apple.requestToken(); | ||
this.deezer = new Deezer(this, this.options) | ||
this.client = client; | ||
this._nodes = nodes; | ||
this.nodes = new Map(); | ||
this.players = new Map(); | ||
this.voiceStates = new Map(); | ||
this.voiceServers = new Map(); | ||
this.isReady = false; | ||
this.user = null; | ||
this.options = options; | ||
this.shards = options.shards || 1; | ||
this.sendData = null; | ||
this.version = config.version; | ||
this.spotify = new Spotify(this, this.options); | ||
this.apple = new AppleMusic(this, this.options); | ||
this.apple.requestToken(); | ||
this.deezer = new Deezer(this, this.options); | ||
} | ||
} | ||
init(client) { | ||
if (this.isReady) return this; | ||
this.user = client.user.id; | ||
this.sendData = (data) => { | ||
const guild = client.guilds.cache.get(data.d.guild_id); | ||
if (guild) guild.shard.send(data); | ||
}; | ||
init(client) { | ||
client.on("raw", async (packet) => { | ||
await this.packetUpdate(packet); | ||
}); | ||
if (this.isReady) return this; | ||
this._nodes.forEach((node) => this.addNode(node)); | ||
} | ||
this.user = client.user.id; | ||
this.sendData = (data) => { | ||
const guild = client.guilds.cache.get(data.d.guild_id); | ||
if (guild) guild.shard.send(data); | ||
} | ||
client.on("raw", async packet => { | ||
await this.packetUpdate(packet); | ||
}) | ||
this._nodes.forEach((node) => this.addNode(node)); | ||
//create a node and connect it with lavalink | ||
addNode(options) { | ||
const node = new Node(this, options, this.options); | ||
if (options.name) { | ||
this.nodes.set(options.name || options.host, node); | ||
node.connect(); | ||
return node; | ||
} | ||
this.nodes.set(options.host, node); | ||
node.connect(); | ||
return node; | ||
} | ||
//remove node and destroy web socket connection | ||
removeNode(identifier) { | ||
if (!identifier) | ||
throw new Error( | ||
`[Poru Error] Provide identifier as a parameter of removeNode` | ||
); | ||
const node = this.nodes.get(identifier); | ||
if (!node) return; | ||
node.destroy(); | ||
this.nodes.delete(identifier); | ||
} | ||
get leastUsedNodes() { | ||
return [...this.nodes.values()] | ||
.filter((node) => node.isConnected) | ||
.sort((a, b) => { | ||
const aLoad = a.stats.cpu | ||
? (a.stats.cpu.systemLoad / a.stats.cpu.cores) * 100 | ||
: 0; | ||
const bLoad = b.stats.cpu | ||
? (b.stats.cpu.systemLoad / b.stats.cpu.cores) * 100 | ||
: 0; | ||
return aLoad - bLoad; | ||
}); | ||
} | ||
getNode(identifier = "best") { | ||
if (!this.nodes.size) throw new Error(`No nodes avaliable currently`); | ||
if (identifier === "best") return this.leastUsedNodes(); | ||
//create a node and connect it with lavalink | ||
addNode(options) { | ||
const node = new Node(this, options, this.options); | ||
if (options.name) { | ||
this.nodes.set(options.name || options.host, node); | ||
node.connect(); | ||
return node; | ||
} | ||
this.nodes.set(options.host, node); | ||
node.connect(); | ||
return node; | ||
} | ||
const node = this.nodes.get(indetifier); | ||
if (!node) throw new Error("The node identifier you provided is not found"); | ||
if (!node.isConnected) node.connect(); | ||
return node; | ||
} | ||
//remove node and destroy web socket connection | ||
removeNode(identifier) { | ||
if (!identifier) throw new Error(`[Poru Error] Provide identifier as a parameter of removeNode`) | ||
const node = this.nodes.get(identifier); | ||
if (!node) return; | ||
node.destroy(); | ||
this.nodes.delete(identifier) | ||
} | ||
createConnection(options) { | ||
this.checkConnection(options); | ||
const player = this.players.get(options.guildId); | ||
if (player) return player; | ||
get leastUsedNodes() { | ||
return [...this.nodes.values()] | ||
.filter((node) => node.isConnected) | ||
.sort((a, b) => { | ||
const aLoad = a.stats.cpu ? (a.stats.cpu.systemLoad / a.stats.cpu.cores) * 100 : 0; | ||
const bLoad = b.stats.cpu ? (b.stats.cpu.systemLoad / b.stats.cpu.cores) * 100 : 0; | ||
return aLoad - bLoad; | ||
}); | ||
} | ||
if (this.leastUsedNodes.length === 0) | ||
throw new Error("[Poru Error] No nodes are avaliable"); | ||
const node = this.nodes.get( | ||
this.leastUsedNodes[0].name || this.leastUsedNodes[0].host | ||
); | ||
if (!node) throw new Error("[Poru Error] No nodes are avalible"); | ||
return this.#createPlayer(node, options); | ||
} | ||
getNode(identifier = "best") { | ||
if (!this.nodes.size) throw new Error(`No nodes avaliable currently`) | ||
if (identifier === "best") return this.leastUsedNodes(); | ||
removeConnection(guildId) { | ||
this.players.get(guildId)?.destroy(); | ||
} | ||
const node = this.nodes.get(indetifier); | ||
if (!node) throw new Error('The node identifier you provided is not found'); | ||
if (!node.isConnected) node.connect(); | ||
return node; | ||
} | ||
checkConnection(options) { | ||
let { guildId, voiceChannel, textChannel, shardId } = options; | ||
if (!guildId) | ||
throw new Error(`[Poru Connection] you have to Provide guildId`); | ||
if (!voiceChannel) | ||
throw new Error(`[Poru Connection] you have to Provide voiceChannel`); | ||
if (!textChannel) | ||
throw new Error(`[Poru Connection] you have to Provide texteChannel`); | ||
// if(shardId == null) throw new Error(`[Poru Connection] You must have to Provide shardId`); | ||
if (typeof guildId !== "string") | ||
throw new Error(`[Poru Connection] guildId must be provided as a string`); | ||
if (typeof voiceChannel !== "string") | ||
throw new Error( | ||
`[Poru Connection] voiceChannel must be provided as a string` | ||
); | ||
if (typeof textChannel !== "string") | ||
throw new Error( | ||
`[Poru Connection] textChannel must be provided as a string` | ||
); | ||
// if(typeof shardId !=="number") throw new Error(`[Poru Connection] shardId must be provided as a number`); | ||
} | ||
createConnection(options) { | ||
this.checkConnection(options) | ||
const player = this.players.get(options.guildId); | ||
if (player) return player; | ||
#createPlayer(node, options) { | ||
if (this.players.has(options.guildId)) | ||
return this.players.get(options.guildId); | ||
if (this.leastUsedNodes.length === 0) throw new Error("[Poru Error] No nodes are avaliable"); | ||
const node = this.nodes.get(this.leastUsedNodes[0].name || this.leastUsedNodes[0].host); | ||
if (!node) throw new Error("[Poru Error] No nodes are avalible"); | ||
const player = new Player(this, node, options); | ||
this.players.set(options.guildId, player); | ||
player.connect(options); | ||
return player; | ||
} | ||
return this.#createPlayer(node, options); | ||
setServersUpdate(data) { | ||
let guild = data.guild_id; | ||
this.voiceServers.set(guild, data); | ||
const server = this.voiceServers.get(guild); | ||
const state = this.voiceStates.get(guild); | ||
if (!server) return false; | ||
const player = this.players.get(guild); | ||
if (!player) return false; | ||
player.updateSession({ | ||
sessionId: state ? state.session_id : player.voiceUpdateState.sessionId, | ||
event: server, | ||
}); | ||
return true; | ||
} | ||
} | ||
setStateUpdate(data) { | ||
if (data.user_id !== this.user) return; | ||
if (data.channel_id) { | ||
const guild = data.guild_id; | ||
removeConnection(guildId) { | ||
this.players.get(guildId)?.destroy(); | ||
} | ||
this.voiceStates.set(data.guild_id, data); | ||
const server = this.voiceServers.get(guild); | ||
const state = this.voiceStates.get(guild); | ||
if (!server) return false; | ||
const player = this.players.get(guild); | ||
if (!player) return false; | ||
player.updateSession({ | ||
sessionId: state ? state.session_id : player.voiceUpdateState.sessionId, | ||
event: server, | ||
}); | ||
checkConnection(options) { | ||
let { guildId, voiceChannel, textChannel, shardId } = options; | ||
if (!guildId) throw new Error(`[Poru Connection] you have to Provide guildId`) | ||
if (!voiceChannel) throw new Error(`[Poru Connection] you have to Provide voiceChannel`) | ||
if (!textChannel) throw new Error(`[Poru Connection] you have to Provide texteChannel`); | ||
// if(shardId == null) throw new Error(`[Poru Connection] You must have to Provide shardId`); | ||
if (typeof guildId !== "string") throw new Error(`[Poru Connection] guildId must be provided as a string`); | ||
if (typeof voiceChannel !== "string") throw new Error(`[Poru Connection] voiceChannel must be provided as a string`); | ||
if (typeof textChannel !== "string") throw new Error(`[Poru Connection] textChannel must be provided as a string`); | ||
// if(typeof shardId !=="number") throw new Error(`[Poru Connection] shardId must be provided as a number`); | ||
return true; | ||
} | ||
this.voiceServers.delete(data.guild_id); | ||
this.voiceStates.delete(data.guild_id); | ||
} | ||
packetUpdate(packet) { | ||
if (!["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(packet.t)) | ||
return; | ||
const player = this.players.get(packet.d.guild_id); | ||
if (!player) return; | ||
#createPlayer(node, options) { | ||
if (this.players.has(options.guildId)) return this.players.get(options.guildId); | ||
const player = new Player(this, node, options); | ||
this.players.set(options.guildId, player); | ||
player.connect(options) | ||
return player; | ||
if (packet.t === "VOICE_SERVER_UPDATE") { | ||
this.setServersUpdate(packet.d); | ||
} | ||
setServersUpdate(data) { | ||
let guild = data.guild_id | ||
this.voiceServers.set(guild, data); | ||
const server = this.voiceServers.get(guild); | ||
const state = this.voiceStates.get(guild); | ||
if (!server) return false; | ||
const player = this.players.get(guild); | ||
if (!player) return false; | ||
player.updateSession({ | ||
sessionId: state ? state.session_id : player.voiceUpdateState.sessionId, | ||
event: server, | ||
}); | ||
return true; | ||
if (packet.t === "VOICE_STATE_UPDATE") { | ||
this.setStateUpdate(packet.d); | ||
} | ||
} | ||
setStateUpdate(data) { | ||
if (data.user_id !== this.user) return; | ||
if (data.channel_id) { | ||
const guild = data.guild_id; | ||
async resolve(query, source) { | ||
const node = this.leastUsedNodes[0]; | ||
if (!node) throw new Error("No nodes are available."); | ||
const regex = /^https?:\/\//; | ||
this.voiceStates.set(data.guild_id, data); | ||
const server = this.voiceServers.get(guild); | ||
const state = this.voiceStates.get(guild); | ||
if (!server) return false; | ||
const player = this.players.get(guild); | ||
if (!player) return false; | ||
player.updateSession({ | ||
sessionId: state ? state.session_id : player.voiceUpdateState.sessionId, | ||
event: server, | ||
}); | ||
return true; | ||
} | ||
this.voiceServers.delete(data.guild_id); | ||
this.voiceStates.delete(data.guild_id); | ||
if (regex.test(query)) { | ||
return this.fetchURL(node, query, source); | ||
} else { | ||
return this.fetchTrack(node, query, source); | ||
} | ||
} | ||
packetUpdate(packet) { | ||
if (!['VOICE_STATE_UPDATE', 'VOICE_SERVER_UPDATE'].includes(packet.t)) return; | ||
const player = this.players.get(packet.d.guild_id); | ||
if (!player) return; | ||
if (packet.t === "VOICE_SERVER_UPDATE") { | ||
this.setServersUpdate(packet.d); | ||
} | ||
if (packet.t === "VOICE_STATE_UPDATE") { | ||
this.setStateUpdate(packet.d); | ||
} | ||
async fetchURL(node, track, source) { | ||
if (this.spotify.check(track)) { | ||
return await this.spotify.resolve(track); | ||
} else if (this.apple.check(track)) { | ||
return await this.apple.resolve(track); | ||
} else if (this.deezer.check(track)) { | ||
return await this.deezer.resolve(track); | ||
} else { | ||
const result = await this.#fetch( | ||
node, | ||
"loadtracks", | ||
`identifier=${encodeURIComponent(track)}` | ||
); | ||
if (!result) throw new Error("[Poru Error] No tracks found."); | ||
return new Response(result); | ||
} | ||
} | ||
async resolve(query, source) { | ||
const node = this.leastUsedNodes[0]; | ||
if (!node) throw new Error("No nodes are available."); | ||
const regex = /^https?:\/\//; | ||
if (regex.test(query)) { | ||
return this.fetchURL(node, query, source) | ||
} else { | ||
return this.fetchTrack(node, query, source) | ||
} | ||
async fetchTrack(node, query, source) { | ||
switch (source) { | ||
case "spotify": { | ||
return this.spotify.fetch(query); | ||
} | ||
case "applemusic": { | ||
return this.apple.fetch(query); | ||
} | ||
case "deezer": { | ||
return this.deezer.fetch(query); | ||
} | ||
default: { | ||
let track = `${source || "ytsearch"}:${query}`; | ||
const result = await this.#fetch( | ||
node, | ||
"loadtracks", | ||
`identifier=${encodeURIComponent(track)}` | ||
); | ||
if (!result) throw new Error("[Poru Error] No tracks found."); | ||
return new Response(result); | ||
} | ||
} | ||
} | ||
async fetchURL(node, track, source) { | ||
async decodeTrack(track) { | ||
const node = this.leastUsedNodes[0]; | ||
if (!node) throw new Error("No nodes are available."); | ||
const result = await this.#fetch(node, "decodetrack", `track=${track}`); | ||
if (result.status === 500) return null; | ||
return result; | ||
} | ||
if (this.spotify.check(track)) { | ||
return await this.spotify.resolve(track); | ||
} else if (this.apple.check(track)) { | ||
return await this.apple.resolve(track); | ||
} else if (this.deezer.check(track)) { | ||
return await this.deezer.resolve(track); | ||
} else { | ||
const result = await this.#fetch(node, "loadtracks", `identifier=${encodeURIComponent(track)}`); | ||
if (!result) throw new Error("[Poru Error] No tracks found."); | ||
return new Response(result); | ||
} | ||
} | ||
#fetch(node, endpoint, param) { | ||
return fetch( | ||
`http${node.secure ? "s" : ""}://${node.host}:${ | ||
node.port | ||
}/${endpoint}?${param}`, | ||
{ | ||
headers: { | ||
Authorization: node.password, | ||
}, | ||
} | ||
) | ||
.then((r) => r.json()) | ||
.catch((e) => { | ||
throw new Error( | ||
`[Poru Error] Failed to fetch from the lavalink.\n error: ${e}` | ||
); | ||
}); | ||
} | ||
async fetchTrack(node, query, source) { | ||
switch (source) { | ||
case "spotify": { | ||
return this.spotify.fetch(query) | ||
} | ||
case "applemusic": { | ||
return this.apple.fetch(query) | ||
} | ||
case "deezer": { | ||
return this.deezer.fetch(query); | ||
} | ||
default: | ||
{ | ||
let track = `${source || "ytsearch"}:${query}`; | ||
const result = await this.#fetch(node, "loadtracks", `identifier=${encodeURIComponent(track)}`); | ||
if (!result) throw new Error("[Poru Error] No tracks found."); | ||
return new Response(result); | ||
} | ||
} | ||
} | ||
async decodeTrack(track) { | ||
const node = this.leastUsedNodes[0]; | ||
if (!node) throw new Error("No nodes are available."); | ||
const result = await this.#fetch(node, "decodetrack", `track=${track}`); | ||
if (result.status === 500) return null; | ||
return result; | ||
} | ||
#fetch(node, endpoint, param) { | ||
return fetch(`http${node.secure ? "s" : ""}://${node.host}:${node.port}/${endpoint}?${param}`, { | ||
headers: { | ||
Authorization: node.password, | ||
}, | ||
}) | ||
.then((r) => r.json()) | ||
.catch((e) => { | ||
throw new Error(`[Poru Error] Failed to fetch from the lavalink.\n error: ${e}`); | ||
}); | ||
} | ||
get(guildId) { | ||
return this.players.get(guildId); | ||
} | ||
get(guildId) { | ||
return this.players.get(guildId); | ||
} | ||
} | ||
module.exports = Poru | ||
module.exports = Poru; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1737
141
9
58319