poru 1.2.3 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,258 +1,252 @@
1
1
  const fetch = (...args) => import('node-fetch').then(({
2
- default: fetch
2
+ default: fetch
3
3
  }) => fetch(...args));
4
- class Spotify {
5
- constructor(manager) {
6
- this.manager = manager;
7
- this.baseURL = "https://api.spotify.com/v1"
8
- this.spotifyPattern = /^(?:https:\/\/open\.spotify\.com\/(?:user\/[A-Za-z0-9]+\/)?|spotify:)(album|playlist|track|artist)(?:[/:])([A-Za-z0-9]+).*$/
9
- this.clientID = manager.options.spotify.clientID;
10
- this.clientSecret = manager.options.spotify.clientSecret
11
- this.authorization = Buffer
12
- .from(`${this.clientID}:${this.clientSecret}`)
13
- .toString("base64");
14
- this.interval = 0;
15
- }
16
-
17
- check(url) {
18
- return this.spotifyPattern.test(url);
19
- }
20
-
21
- async requestToken() {
22
- if (this.nextRequest) return;
23
-
24
- try {
25
- const data = await fetch("https://accounts.spotify.com/api/token?grant_type=client_credentials",{
26
- method:"POST",
27
- headers: {
28
- Authorization: `Basic ${this.authorization}`,
29
- 'Content-Type': 'application/x-www-form-urlencoded',
30
- }
31
- })
32
-
33
- const body = await data.json();
34
-
35
- this.token = `Bearer ${body.access_token}`;
36
- this.interval = body.expires_in * 1000
37
- } catch (e) {
38
- if (e.status === 400) {
39
- throw new Error("Invalid Spotify client.")
40
- }
41
- }
42
- }
43
-
44
- async renew() {
45
- if (Date.now() >= this.interval) {
46
- await this.requestToken();
47
- }
48
- }
49
-
50
- async requestData(endpoint) {
51
- await this.renew();
52
-
53
- const req = await fetch(`${this.baseURL}${/^\//.test(endpoint) ? endpoint : `/${endpoint}`}`, {
54
- headers: { Authorization: this.token }
55
- })
56
- const data = await req.json()
57
- return data
58
- }
59
-
60
-
61
- async resolve(url) {
62
- if (!this.token) await this.requestToken()
63
- const [, type, id] = await this.spotifyPattern.exec(url) ?? [];
64
-
65
- switch (type) {
66
-
67
- case "playlist":
68
- {
69
- return this.fetchPlaylist(id)
70
- }
71
- case "track":
72
- {
73
- return this.fetchTrack(id)
74
- }
75
- case "album":
76
- {
77
- return this.fetchAlbum(id)
78
- }
79
- case "artist":
80
- {
81
- return this.fetchArtist(id);
82
- }
83
-
84
- default: {
85
- return this.manager.resolve(url)
86
- }
87
- }
88
-
89
- }
90
-
91
- async fetchPlaylist(id) {
92
- try {
93
- const playlist = await this.requestData(`/playlists/${id}`)
94
- await this.fetchPlaylistTracks(playlist);
95
- const unresolvedPlaylistTracks = playlist.tracks.items.map(x => this.buildUnresolved(x.track));
96
-
97
-
98
- return this.buildResponse(
99
- "PLAYLIST_LOADED",
100
- (await Promise.all(unresolvedPlaylistTracks.map(x => x.then((a) => a.resolve())))).filter(Boolean),
101
- playlist.name
102
- );
103
-
104
- } catch (e) {
105
- return this.buildResponse(e.status === 404 ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message);
106
- }
107
- }
108
-
109
- async fetchAlbum(id) {
110
- try{
111
- const album = await this.requestData(`/albums/${id}`)
112
-
113
- const unresolvedPlaylistTracks = album.tracks.map(x => this.buildUnresolved(x));
114
- return this.buildResponse(
115
- "PLAYLIST_LOADED",
116
- (await Promise.all(unresolvedPlaylistTracks.map(x => x.then((a) => a.resolve())))).filter(Boolean),
117
- album.name
118
- );
119
-
120
- }catch(e){
121
- return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message);
122
- }
123
- }
124
-
125
- async fetchArtist(id) {
126
- try{
127
- const artist = await this.requestData(`/artists/${id}`)
128
-
129
- const data = await this.requestData(`/artists/${id}/top-tracks?market=US`)
130
- const unresolvedPlaylistTracks = data.tracks.map(x => this.buildUnresolved(x));
131
-
132
- return this.buildResponse(
133
- "PLAYLIST_LOADED",
134
- (await Promise.all(unresolvedPlaylistTracks.map(x => x.then((a) => a.resolve())))).filter(Boolean),
135
- artist.name
136
- );
137
- }catch(e){
138
- return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message);
139
- }
140
-
141
- }
142
-
143
- async fetchTrack(id) {
144
- try{
145
- const data = await this.requestData(`/tracks/${id}`)
146
- const unresolvedTrack = this.buildUnresolved(data);
147
-
148
- return this.buildResponse(
149
- "TRACK_LOADED",
150
- [await unresolvedTrack.then((a) => a.resolve())]
151
- );
152
- }catch(e){
153
- return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message);
154
- }
155
- }
156
-
157
- async fetchByWords(query) {
158
- try{
159
- const data = await this.requestData(`/search/?q="${query}"&type=artist,album,track`)
160
-
161
- const unresolvedTrack = this.buildUnresolved(data.tracks.items[0]);
162
-
163
- return this.buildResponse(
164
- "TRACK_LOADED",
165
- [await unresolvedTrack.then((a) => a.resolve())]
166
- );
167
- }catch(e){
168
- return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message);
169
- }
170
- }
171
-
172
- async fetchPlaylistTracks(spotifyPlaylist) {
173
- let nextPage = spotifyPlaylist.tracks.next;
174
- let pageLoaded = 1;
175
- while (nextPage) {
176
- if (!nextPage) break;
177
- const req = await fetch(nextPage, {
178
- headers: { Authorization: this.token }
179
- })
180
- const body = await req.json()
181
- if (body.error) break;
182
- spotifyPlaylist.tracks.items.push(...body.items);
183
-
184
- nextPage = body.next;
185
- pageLoaded++;
186
- }
187
- }
188
-
189
-
190
-
191
- async buildUnresolved(track) {
192
- if (!track) throw new ReferenceError("The Spotify track object was not provided");
193
- // if (!track.artists) throw new ReferenceError("The track artists array was not provided");
194
- if (!track.name) throw new ReferenceError("The track name was not provided");
195
- if (!Array.isArray(track.artists)) throw new TypeError(`The track artists must be an array, received type ${typeof track.artists}`);
196
- if (typeof track.name !== "string") throw new TypeError(`The track name must be a string, received type ${typeof track.name}`);
197
-
198
- const _this = this;
199
- return {
200
- track: "",
201
- info: {
202
- sourceName: 'spotify',
203
- identifier: track.id,
204
- isSeekable: true,
205
- author: track.artists[0] ? track.artists[0].name : 'Unknown',
206
- length: track.duration_ms,
207
- isStream: false,
208
- title: track.name,
209
- uri: `https://open.spotify.com/track/${track.id}`,
210
- image: track.album?.images[0]?.url,
211
- },
212
- resolve() {
213
- return _this.buildTrack(this)
214
- }
215
- }
216
-
217
-
218
-
219
- }
220
-
221
- async fetchMetaData(track) {
222
-
223
- const fetch = await this.manager.resolve(`${track.info.title} ${track.info.author}`)
224
- return fetch.tracks[0];
225
- }
226
-
227
- async buildTrack(unresolvedTrack) {
228
- const lavaTrack = await this.fetchMetaData(unresolvedTrack);
229
- if(lavaTrack){
230
- unresolvedTrack.track = lavaTrack.track;
231
- unresolvedTrack.info.identifier = lavaTrack.info.identifier
232
- return unresolvedTrack
233
- }
234
- }
235
-
236
-
237
- compareValue(value) {
238
- return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined';
239
- }
240
-
241
-
242
- buildResponse(loadType, tracks, playlistName, exceptionMsg) {
243
-
244
- return Object.assign({
245
- loadType,
246
- tracks,
247
- playlistInfo: playlistName ? { name: playlistName } : {}
248
- }, exceptionMsg ? { exception: { message: exceptionMsg, severity: "COMMON" } } : {});
249
- }
4
+ let spotifyPattern =
5
+ /^(?:https:\/\/open\.spotify\.com\/(?:user\/[A-Za-z0-9]+\/)?|spotify:)(album|playlist|track|artist)(?:[/:])([A-Za-z0-9]+).*$/;
250
6
 
7
+ const Track = require("../guild/Track")
251
8
 
9
+ class Spotify {
10
+ constructor(manager) {
11
+ this.manager = manager;
12
+ this.baseURL = 'https://api.spotify.com/v1';
13
+ this.options ={
14
+ clientID : manager.options.spotify.clientID,
15
+ clientSecret : manager.options.spotify.clientSecret,
16
+ playlistLimit : manager.options.spotify.playlistLimit,
17
+ albumLimit : manager.options.spotify.albumLimit,
18
+ artistLimit : manager.options.spotify.artistLimit,
19
+ searchMarket : manager.options.spotify.searchMarket
20
+ }
21
+ this.authorization = Buffer.from(`${this.options.clientID}:${this.options.clientSecret}`).toString('base64');
22
+ this.interval = 0;
23
+ }
24
+
25
+ check(url) {
26
+ return spotifyPattern.test(url);
27
+ }
28
+
29
+ async requestToken() {
30
+
31
+ try {
32
+ const data = await fetch('https://accounts.spotify.com/api/token?grant_type=client_credentials', {
33
+ method: 'POST',
34
+ headers: {
35
+ Authorization: `Basic ${this.authorization}`,
36
+ 'Content-Type': 'application/x-www-form-urlencoded',
37
+ },
38
+ });
39
+
40
+ const body = await data.json();
41
+
42
+ this.token = `Bearer ${body.access_token}`;
43
+ this.interval = body.expires_in * 1000;
44
+ } catch (e) {
45
+ if (e.status === 400) {
46
+ throw new Error('Invalid Spotify client.');
47
+ }
48
+ }
49
+ }
50
+
51
+ async renew() {
52
+ if (Date.now() >= this.interval) {
53
+ await this.requestToken();
54
+ }
55
+ }
56
+
57
+ async requestData(endpoint) {
58
+ await this.renew();
59
+
60
+ const req = await fetch(`${this.baseURL}${/^\//.test(endpoint) ? endpoint : `/${endpoint}`}`, {
61
+ headers: { Authorization: this.token },
62
+ });
63
+ const data = await req.json();
64
+ return data;
65
+ }
66
+
67
+ async resolve(url) {
68
+ if (!this.token) await this.requestToken();
69
+ const [, type, id] = spotifyPattern.exec(url) ?? [];
70
+
71
+ switch (type) {
72
+ case 'playlist': {
73
+ return this.fetchPlaylist(id);
74
+ }
75
+ case 'track': {
76
+ return this.fetchTrack(id);
77
+ }
78
+ case 'album': {
79
+ return this.fetchAlbum(id);
80
+ }
81
+ case 'artist': {
82
+ return this.fetchArtist(id);
83
+ }
84
+
85
+
86
+ }
87
+ }
88
+
89
+ async fetchPlaylist(id) {
90
+ try {
91
+ const playlist = await this.requestData(`/playlists/${id}`);
92
+ await this.fetchPlaylistTracks(playlist);
93
+
94
+ const limitedTracks = this.options.playlistLimit
95
+ ? playlist.tracks.items.slice(0, this.options.playlistLimit * 100)
96
+ : playlist.tracks.items;
97
+
98
+ const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x.track)));
99
+
100
+ return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, playlist.name);
101
+ } catch (e) {
102
+ return this.buildResponse(
103
+ e.status === 404 ? 'NO_MATCHES' : 'LOAD_FAILED',
104
+ [],
105
+ undefined,
106
+ e.body?.error.message ?? e.message,
107
+ );
108
+ }
109
+ }
110
+
111
+ async fetchAlbum(id) {
112
+ try {
113
+ const album = await this.requestData(`/albums/${id}`);
114
+
115
+ const limitedTracks = this.options.albumLimit ? album.tracks.items.slice(0, this.options.albumLimit * 100) : album.tracks.items;
116
+
117
+ const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)));
118
+ return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, album.name);
119
+ } catch (e) {
120
+ return this.buildResponse(
121
+ e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED',
122
+ [],
123
+ undefined,
124
+ e.body?.error.message ?? e.message,
125
+ );
126
+ }
127
+ }
128
+
129
+ async fetchArtist(id) {
130
+ try {
131
+ const artist = await this.requestData(`/artists/${id}`);
132
+
133
+ const data = await this.requestData(`/artists/${id}/top-tracks?market=${this.searchMarket ?? 'US'}`);
134
+
135
+ const limitedTracks = this.options.artistLimit ? data.tracks.slice(0, this.options.artistLimit * 100) : data.tracks;
136
+
137
+ const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)));
138
+
139
+ return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, artist.name);
140
+ } catch (e) {
141
+ return this.buildResponse(
142
+ e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED',
143
+ [],
144
+ undefined,
145
+ e.body?.error.message ?? e.message,
146
+ );
147
+ }
148
+ }
149
+
150
+ async fetchTrack(id) {
151
+ try {
152
+ const data = await this.requestData(`/tracks/${id}`);
153
+ const unresolvedTrack = await this.buildUnresolved(data);
154
+ return this.buildResponse('TRACK_LOADED', [unresolvedTrack]);
155
+ } catch (e) {
156
+ return this.buildResponse(
157
+ e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED',
158
+ [],
159
+ undefined,
160
+ e.body?.error.message ?? e.message,
161
+ );
162
+ }
163
+ }
164
+
165
+ async fetch(query) {
166
+ try {
167
+ if (this.check(query)) return this.resolve(query);
168
+
169
+ const data = await this.requestData(
170
+ `/search/?q="${query}"&type=artist,album,track&market=${this.options.searchMarket ?? 'US'}`,
171
+ );
172
+
173
+ const unresolvedTrack = await this.buildUnresolved(data.tracks.items[0]);
174
+
175
+ return this.buildResponse('TRACK_LOADED', [unresolvedTrack]);
176
+ } catch (e) {
177
+ return this.buildResponse(
178
+ e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED',
179
+ [],
180
+ undefined,
181
+ e.body?.error.message ?? e.message,
182
+ );
183
+ }
184
+ }
185
+
186
+ async fetchPlaylistTracks(spotifyPlaylist) {
187
+ let nextPage = spotifyPlaylist.tracks.next;
188
+ let pageLoaded = 1;
189
+ while (nextPage) {
190
+ if (!nextPage) break;
191
+ const req = await fetch(nextPage, {
192
+ headers: { Authorization: this.token },
193
+ });
194
+ const body = await req.json();
195
+ if (body.error) break;
196
+ spotifyPlaylist.tracks.items.push(...body.items);
197
+
198
+ nextPage = body.next;
199
+ pageLoaded++;
200
+ }
201
+ }
202
+
203
+ async buildUnresolved(track) {
204
+ if (!track) throw new ReferenceError('The Spotify track object was not provided');
205
+
206
+ return new Track({
207
+ track: '',
208
+ info: {
209
+ sourceName: 'spotify',
210
+ identifier: track.id,
211
+ isSeekable: true,
212
+ author: track.artists[0] ? track.artists[0].name : 'Unknown',
213
+ length: track.duration_ms,
214
+ isStream: false,
215
+ title: track.name,
216
+ uri: `https://open.spotify.com/track/${track.id}`,
217
+ image: track.album?.images[0]?.url,
218
+ },
219
+ });
220
+ }
221
+
222
+ async fetchMetaData(track) {
223
+ const fetch = await this.manager.resolve(`${track.info.title} ${track.info.author}`);
224
+ return fetch.tracks[0];
225
+ }
226
+
227
+ async buildTrack(unresolvedTrack) {
228
+ const lavaTrack = await this.fetchMetaData(unresolvedTrack);
229
+ if (lavaTrack) {
230
+ unresolvedTrack.track = lavaTrack.track;
231
+ unresolvedTrack.info.identifier = lavaTrack.info.identifier;
232
+ return unresolvedTrack;
233
+ }
234
+ }
235
+
236
+ compareValue(value) {
237
+ return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined';
238
+ }
239
+
240
+ buildResponse(loadType, tracks, playlistName, exceptionMsg) {
241
+ return Object.assign(
242
+ {
243
+ loadType,
244
+ tracks,
245
+ playlistInfo: playlistName ? { name: playlistName } : {},
246
+ },
247
+ exceptionMsg ? { exception: { message: exceptionMsg, severity: 'COMMON' } } : {},
248
+ );
249
+ }
252
250
  }
253
251
 
254
-
255
-
256
-
257
-
258
- module.exports = Spotify
252
+ module.exports = Spotify;
@@ -1,8 +1,7 @@
1
- import { EventEmitter } from "ws";
1
+ import { EventEmitter, WebSocket } from "ws";
2
2
 
3
3
  declare module'Poru' {
4
4
 
5
- import { EventEmitter } from 'events';
6
5
 
7
6
  export class Poru extends EventEmitter {
8
7
  constructor(client: typeof EventEmitter| any,nodes:[],options:{})
@@ -56,48 +55,45 @@ export class Node {
56
55
  constructor(manager:typeof EventEmitter|any,options:object,node:object)
57
56
 
58
57
  public manager: EventEmitter
59
- public name: String
60
- public host: String
61
- public port : Number
62
- public url : String
63
- public password: String
64
- public secure: Boolean
65
- public ws = any
66
- public reconnectTime : Number
67
- public resumeKey:String|Number;
68
- public resumeTimeout =Number
69
- public reconnectAttempt:Number;
70
- public reconnects:Number;
71
- public isConnected:Boolean;
72
- public destroyed:Boolean;
58
+ public name: string
59
+ public host: string
60
+ public port : number
61
+ public url : string
62
+ public password: string
63
+ public secure: boolean
64
+ public ws: WebSocket
65
+ public reconnectTime : number
66
+ public resumeKey:string|number;
67
+ public resumeTimeout: number
68
+ public reconnectAttempt:number;
69
+ public reconnects:number;
70
+ public isConnected:boolean;
71
+ public destroyed:boolean;
73
72
  public stats:Object
74
73
 
75
74
 
76
75
  connect():void
77
76
 
78
- destroy()
77
+ destroy(): void
79
78
 
80
- reconnect()
79
+ reconnect(): void
81
80
 
82
- send(playload:any)
81
+ send(playload:Object): void | Object | String
83
82
 
84
83
  get penalties():Number
85
84
 
86
-
87
-
88
85
  }
89
86
 
90
87
 
91
88
  export class Player extends EventEmitter {
92
- constructor(manager:EventEmitter,node:Map|Object, options:Object)
93
-
89
+ constructor(manager:EventEmitter,node:Map<any, any>|Object, options:Object)
94
90
 
95
91
 
96
92
  public manager:EventEmitter
97
93
 
98
- public queue:Array |any
94
+ public queue:Array<any> |any
99
95
 
100
- public node: Map|Object|Any;
96
+ public node: Map<any, any>|Object|Any;
101
97
 
102
98
  public filters: any
103
99
 
@@ -107,15 +103,15 @@ export class Player extends EventEmitter {
107
103
 
108
104
  public textChannel:any;
109
105
 
110
- public isConnectd = Boolean;
106
+ public isConnected: boolean;
111
107
 
112
- public isPlaying:Boolean;
108
+ public isPlaying:boolean;
113
109
 
114
- public isPause:Boolean;
110
+ public isPaused:boolean;
115
111
 
116
- public trackRepeat:Boolean;
112
+ public trackRepeat:boolean;
117
113
 
118
- public queueRepeat:Boolean;
114
+ public queueRepeat:boolean;
119
115
 
120
116
  public loop:Number;
121
117
 
@@ -127,9 +123,7 @@ export class Player extends EventEmitter {
127
123
 
128
124
  public previousTrack:Object;
129
125
 
130
- public voiceUpdateState = any;
131
-
132
-
126
+ public voiceUpdateState:any;
133
127
 
134
128
 
135
129
  play()
@@ -158,24 +152,8 @@ export class Player extends EventEmitter {
158
152
 
159
153
  disconnect()
160
154
 
161
-
162
155
  destroy()
163
156
 
164
157
  autoplay(toggle:Boolean)
165
158
 
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
- }
159
+ }
package/src/config.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "client" :"Poru"
3
- }