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.
package/src/config.js ADDED
@@ -0,0 +1,20 @@
1
+ module.exports = {
2
+ clientName: "Poru",
3
+ autoResume : true,
4
+ version :"2.0",
5
+ OPCodes: {
6
+ CONFIGURE_RESUMING : 'configureResuming',
7
+ DESTROY : 'destroy',
8
+ FILTERS : 'filters',
9
+ EVENT : 'event',
10
+ PAUSE : 'pause',
11
+ PLAY : 'play',
12
+ PLAYER_UPDATE : 'playerUpdate',
13
+ SEEK : 'seek',
14
+ STATS : 'stats',
15
+ STOP : 'stop',
16
+ VOICE_UPDATE : 'voiceUpdate',
17
+ VOLUME : 'volume'
18
+
19
+ }
20
+ }
@@ -29,8 +29,8 @@ class Filters {
29
29
  return this;
30
30
  }
31
31
  setTimescale(timescale) {
32
- this.updateFilters();
33
32
  this.timescale = timescale || null;
33
+ this.updateFilters();
34
34
  return this;
35
35
  }
36
36
  setTremolo(tremolo) {
@@ -90,7 +90,7 @@ class Filters {
90
90
  this.doubleTime = false;
91
91
  this.vaporwave = false;
92
92
  }
93
- return this
93
+ return val;
94
94
  }
95
95
 
96
96
  setSlowmode(val) {
@@ -141,7 +141,6 @@ class Filters {
141
141
  }
142
142
 
143
143
  updateFilters(){
144
- console.log(this.node.send)
145
144
  const { volume, equalizer, karaoke, timescale, tremolo, vibrato, rotation, distortion, channelMix, lowPass } = this;
146
145
  this.node.send({
147
146
  op:"filters",
@@ -165,4 +164,4 @@ class Filters {
165
164
 
166
165
 
167
166
 
168
- module.exports = Filters;
167
+ module.exports = Filters;
@@ -0,0 +1,61 @@
1
+ const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2
+ class PoruTrack {
3
+ constructor(data) {
4
+ this.track = data.track
5
+ this.info = {
6
+ identifier: null,
7
+ isSeekable: data.info.isSeekable,
8
+ author: data.info.author,
9
+ length: data.info.length,
10
+ isStream: data.info.isStream,
11
+ sourceName: data.info.sourceName,
12
+ title: data.info.title,
13
+ uri: data.info.uri,
14
+ image: data.info.image || null
15
+
16
+ }
17
+ }
18
+
19
+ async resolve(manager) {
20
+
21
+ const query = [this.info.author, this.info.title].filter((x) => !!x).join(' - ');
22
+
23
+
24
+ const result = await manager.resolve(query);
25
+ if (!result || !result.tracks.length) return;
26
+
27
+ if (this.info.author) {
28
+ const author = [this.info.author, `${this.info.author} - Topic`];
29
+ const officialAudio = result.tracks.find(
30
+ (track) =>
31
+ author.some((name) => new RegExp(`^${escapeRegExp(name)}$`, 'i').test(track.info.author)) ||
32
+ new RegExp(`^${escapeRegExp(this.info.title)}$`, 'i').test(track.info.title),
33
+ );
34
+ if (officialAudio) {
35
+ this.info.identifier = officialAudio.info.identifier
36
+ this.track = officialAudio.track;
37
+ this.info.length = officialAudio.info.length
38
+ return this
39
+ }
40
+ }
41
+ if (this.info.length) {
42
+ const sameDuration = result.tracks.find(
43
+ (track) =>
44
+ track.info.length >= (this.info.length ? this.length : 0) - 2000 &&
45
+ track.info.length <= (this.info.length ? this.length : 0) + 2000,
46
+ );
47
+ if (sameDuration) {
48
+ this.info.identifier = sameDuration.info.identifier
49
+ this.track = sameDuration.track;
50
+ this.info.length = sameDuration.length
51
+ return this
52
+ }
53
+ }
54
+ this.info.identifier = result.tracks[0].info.identifier
55
+ this.track = result.tracks[0].track;
56
+ this.info.length = result.tracks[0].info.length
57
+ return this
58
+ }
59
+ }
60
+
61
+ module.exports = PoruTrack;
@@ -1,19 +1,61 @@
1
+ const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1
2
  class Track {
2
- constructor(data) {
3
- this.track = data.track
4
- this.info ={
5
- identifier : data.info.identifier,
6
- isSeekable : data.info.isSeekable,
7
- author : data.info.author,
8
- length : data.info.length,
9
- isStream : data.info.isStream,
10
- sourceName:data.info.sourceName,
11
- title : data.info.title,
12
- uri : data.info.uri,
13
- image : `https://i.ytimg.com/vi/${data.info.identifier}/maxresdefault.jpg` || null
14
-
3
+ constructor(data) {
4
+ this.track = data.track
5
+ this.info = {
6
+ identifier: data.info.identifier,
7
+ isSeekable: data.info.isSeekable,
8
+ author: data.info.author,
9
+ length: data.info.length,
10
+ isStream: data.info.isStream,
11
+ sourceName: data.info.sourceName,
12
+ title: data.info.title,
13
+ uri: data.info.uri,
14
+ image: `https://i.ytimg.com/vi/${data.info.identifier}/maxresdefault.jpg` || null
15
+
16
+ }
17
+ }
18
+
19
+ async resolve(manager) {
20
+
21
+ const query = [this.info.author, this.info.title].filter((x) => !!x).join(' - ');
22
+
23
+
24
+ const result = await manager.resolve(query);
25
+ if (!result || !result.tracks.length) return;
26
+
27
+ if (this.info.author) {
28
+ const author = [this.info.author, `${this.info.author} - Topic`];
29
+ const officialAudio = result.tracks.find(
30
+ (track) =>
31
+ author.some((name) => new RegExp(`^${escapeRegExp(name)}$`, 'i').test(track.info.author)) ||
32
+ new RegExp(`^${escapeRegExp(this.info.title)}$`, 'i').test(track.info.title),
33
+ );
34
+ if (officialAudio) {
35
+ this.info.identifier = officialAudio.info.identifier
36
+ this.image =`https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg`
37
+ this.track = officialAudio.track;
38
+ return this
15
39
  }
40
+ }
41
+ if (this.info.length) {
42
+ const sameDuration = result.tracks.find(
43
+ (track) =>
44
+ track.info.length >= (this.info.length ? this.length : 0) - 2000 &&
45
+ track.info.length <= (this.info.length ? this.length : 0) + 2000,
46
+ );
47
+ if (sameDuration) {
48
+ this.info.identifier = sameDuration.info.identifier
49
+ this.image =`https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg`
50
+ this.track = sameDuration.track;
51
+ return this
16
52
  }
53
+ }
54
+ this.info.identifier = result.tracks[0].info.identifier
55
+ this.image =`https://i.ytimg.com/vi/${this.info.identifier}/maxresdefault.jpg`
56
+ this.track = result.tracks[0].track;
57
+ return this
58
+ }
17
59
  }
18
60
 
19
61
  module.exports = Track;
@@ -0,0 +1,248 @@
1
+ const fetch = (...args) => import('node-fetch').then(({
2
+ default: fetch
3
+ }) => fetch(...args));
4
+ const PoruTrack = require("../guild/PoruTrack")
5
+ let baseURL = /(?:https:\/\/music\.apple\.com\/)(?:.+)?(artist|album|music-video|playlist)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)/;
6
+
7
+ class AppleMusic {
8
+ constructor(manager, options) {
9
+ this.manager = manager;
10
+ this.options = {
11
+ playlistLimit: options.apple.playlistLimit || null,
12
+ albumLimit: options.apple.albumLimit || null,
13
+ artistLimit: options.apple.artistLimit || null,
14
+ searchMarket: options.apple.searchMarket || "us",
15
+ imageHeight: options.apple.imageHeight || 500,
16
+ imageWeight: options.apple.imageWeight || 500,
17
+ }
18
+ this.url = `https://amp-api.music.apple.com/v1/catalog/${this.options.searchMarket}`
19
+ this.token = null;
20
+
21
+ }
22
+
23
+ check(url) {
24
+ return baseURL.test(url);
25
+ }
26
+
27
+
28
+ async requestToken() {
29
+ try {
30
+
31
+ let req = await fetch('https://music.apple.com/us/browse');
32
+ let json = await req.text();
33
+ let config = /<meta name="desktop-music-app\/config\/environment" content="(.*?)">/.exec(json);
34
+
35
+ let key = config = JSON.parse(decodeURIComponent(config[1]));
36
+ let { token } = key?.MEDIA_API
37
+
38
+ if (!token) throw new Error("No acess key found for apple music")
39
+
40
+ this.token = `Bearer ${token}`;
41
+ } catch (e) {
42
+ if (e.status === 400) {
43
+ throw new Error(`[Poru Apple Music]:${e}`);
44
+ }
45
+ }
46
+ }
47
+
48
+
49
+ async requestData(param) {
50
+ if (!this.token) await this.requestToken();
51
+
52
+ let req = await fetch(`${this.url}${param}`, {
53
+ headers: {
54
+ Authorization: `${this.token}`,
55
+ origin: 'https://music.apple.com'
56
+ }
57
+ })
58
+
59
+ let body = await req.json();
60
+
61
+ return body;
62
+ }
63
+
64
+
65
+
66
+
67
+
68
+ async resolve(url) {
69
+ let [, type, id] = await baseURL.exec(url)
70
+
71
+ switch (type) {
72
+ case "playlist": {
73
+ return this.fetchPlaylist(url);
74
+ }
75
+ case "album": {
76
+ return this.fetchAlbum(url);
77
+ }
78
+ case "artist": {
79
+ return this.fetchArtist(url);
80
+ }
81
+ }
82
+ }
83
+
84
+ async fetch(query) {
85
+ if (this.check(query)) return this.resolve(query);
86
+
87
+ try {
88
+
89
+ let tracks = await this.requestData(`/search?types=songs&term=${query}`)
90
+
91
+ let track = await this.buildUnresolved(tracks.results.songs.data[0])
92
+
93
+ return this.buildResponse('TRACK_LOADED', [track]);
94
+
95
+ } catch (e) {
96
+ return this.buildResponse(
97
+ 'LOAD_FAILED',
98
+ [],
99
+ undefined,
100
+ e.body?.error.message ?? e.message,
101
+ );
102
+
103
+ }
104
+
105
+ }
106
+
107
+ async fetchPlaylist(url) {
108
+ try {
109
+ let query = new URL(url).pathname.split('/');
110
+ let id = query.pop();
111
+ let playlist = await this.requestData(`/playlists/${id}`)
112
+ let name = playlist.data[0].attributes.name
113
+
114
+ const limitedTracks = this.options.playlistLimit
115
+ ? playlist.data[0].relationships.tracks.data.slice(0, this.options.playlistLimit * 100)
116
+ : playlist.data[0].relationships.tracks.data;
117
+
118
+ let tracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)))
119
+ return this.buildResponse('PLAYLIST_LOADED', tracks, name);
120
+ } catch (e) {
121
+ return this.buildResponse(
122
+ 'LOAD_FAILED',
123
+ [],
124
+ undefined,
125
+ e.body?.error.message ?? e.message,
126
+ );
127
+ }
128
+ }
129
+
130
+
131
+
132
+ async fetchAlbum(url) {
133
+
134
+ try {
135
+ let query = new URL(url).pathname.split('/');
136
+ let id = query.pop();
137
+ let album = await this.requestData(`/albums/${id}`)
138
+
139
+
140
+ const limitedTracks = this.options.albumLimit
141
+ ? album.data[0].relationships.tracks.data.slice(0, this.options.albumLimit * 100)
142
+ : album.data[0].relationships.tracks.data;
143
+
144
+
145
+ let name = album.data[0].attributes.name
146
+ let tracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)));
147
+ return this.buildResponse('PLAYLIST_LOADED', tracks, name);
148
+ } catch (e) {
149
+ return this.buildResponse(
150
+ 'LOAD_FAILED',
151
+ [],
152
+ undefined,
153
+ e.body?.error.message ?? e.message,
154
+ );
155
+
156
+ }
157
+ }
158
+
159
+ async fetchArtist(url) {
160
+
161
+ try {
162
+ let query = new URL(url).pathname.split('/');
163
+ let id = query.pop();
164
+ let artist = await this.requestData(`/attists/${id}`)
165
+ let name = artistdata[0].attributes.name
166
+
167
+ const limitedTracks = this.options.artistLimit
168
+ ? artist.data[0].relationships.tracks.data.slice(0, this.options.artist * 100)
169
+ : artist.data[0].relationships.tracks.data;
170
+
171
+ let tracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)));
172
+ return this.buildResponse('PLAYLIST_LOADED', tracks, name);
173
+ } catch (e) {
174
+ return this.buildResponse(
175
+ 'LOAD_FAILED',
176
+ [],
177
+ undefined,
178
+ e.body?.error.message ?? e.message,
179
+ );
180
+
181
+ }
182
+ }
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+ async buildUnresolved(track) {
191
+ if (!track) throw new ReferenceError('The Apple track object was not provided');
192
+
193
+ return new PoruTrack({
194
+ track: '',
195
+ info: {
196
+ sourceName: 'Apple Music',
197
+ identifier: track.id,
198
+ isSeekable: true,
199
+ author: track.attributes.artistName ? track.attributes.artistName : 'Unknown',
200
+ length: track.attributes.durationInMillis,
201
+ isStream: false,
202
+ title: track.attributes.name,
203
+ uri: track.attributes.url,
204
+ image: track.attributes.artwork.url.replace("{w}", this.options.imageWeight).replace("{h}", this.options.imageHeight)
205
+ },
206
+ });
207
+ }
208
+
209
+ compareValue(value) {
210
+ return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined';
211
+ }
212
+
213
+ buildResponse(loadType, tracks, playlistName, exceptionMsg) {
214
+ return Object.assign(
215
+ {
216
+ loadType,
217
+ tracks,
218
+ playlistInfo: playlistName ? { name: playlistName } : {},
219
+ },
220
+ exceptionMsg ? { exception: { message: exceptionMsg, severity: 'COMMON' } } : {},
221
+ );
222
+ }
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+ }
243
+ module.exports = AppleMusic
244
+
245
+
246
+ const apple = new AppleMusic("",{apple:{}})
247
+
248
+ apple.resolve("https://music.apple.com/us/playlist/bollywood-hits/pl.d60caf02fcce4d7e9788fe01243b7c2c")
@@ -0,0 +1,233 @@
1
+ const fetch = (...args) => import('node-fetch').then(({
2
+ default: fetch
3
+ }) => fetch(...args));
4
+ let REGEX = /^(?:https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?(track|album|playlist|artist)\/(\d+)/
5
+
6
+ const PoruTrack = require("../guild/PoruTrack")
7
+
8
+ class Deezer {
9
+ constructor(manager, options) {
10
+
11
+
12
+ this.manager = manager;
13
+ this.baseURL = 'https://api.deezer.com';
14
+ this.options = {
15
+ playlistLimit: options.deezer.playlistLimit || null,
16
+ albumLimit: options.deezer.albumLimit || null,
17
+ artistLimit: options.deezer.artistLimit || null
18
+
19
+ }
20
+ }
21
+
22
+
23
+ check(url) {
24
+ return REGEX.test(url);
25
+ }
26
+
27
+ async requestData(endpoint) {
28
+ const req = await fetch(`${this.baseURL}/${endpoint}`, {
29
+ });
30
+ const data = await req.json();
31
+ return data;
32
+ }
33
+
34
+
35
+ async resolve(url) {
36
+ const [, type, id] = REGEX.exec(url) ?? [];
37
+ switch (type) {
38
+ case 'playlist': {
39
+ return this.fetchPlaylist(id);
40
+ }
41
+ case 'track': {
42
+ return this.fetchTrack(id);
43
+ }
44
+ case 'album': {
45
+ return this.fetchAlbum(id);
46
+ }
47
+ case 'artist': {
48
+ return this.fetchArtist(id);
49
+ }
50
+
51
+ }
52
+
53
+
54
+
55
+ }
56
+
57
+
58
+ async fetchPlaylist(id) {
59
+ try {
60
+ const playlist = await this.requestData(`/playlist/${id}`);
61
+
62
+ const limitedTracks = this.options.playlistLimit
63
+ ? playlist.track.data.slice(0, this.options.playlistLimit * 100)
64
+ : playlist.track.data;
65
+
66
+
67
+ const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)));
68
+ return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, playlist.name);
69
+
70
+ } catch (e) {
71
+ return this.buildResponse(
72
+ 'LOAD_FAILED',
73
+ [],
74
+ undefined,
75
+ e.body?.error.message ?? e.message,
76
+ );
77
+ }
78
+ }
79
+
80
+ async fetchAlbum(id) {
81
+ try {
82
+ const album = await this.requestData(`/album/${id}`);
83
+
84
+ const limitedTracks = this.options.albumLimit
85
+ ? album.track.data.slice(0, this.options.albumLimit * 100)
86
+ : album.track.data;
87
+
88
+
89
+ const unresolvedAlbumTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)));
90
+
91
+ return this.buildResponse('PLAYLIST_LOADED', unresolvedAlbumTracks, album.name);
92
+ } catch (e) {
93
+ return this.buildResponse(
94
+ 'LOAD_FAILED',
95
+ [],
96
+ undefined,
97
+ e.body?.error.message ?? e.message,
98
+ );
99
+
100
+
101
+ }
102
+ }
103
+
104
+ async fetchTrack(id) {
105
+
106
+ try {
107
+ const track = await this.requestData(`/track/${id}`)
108
+
109
+ const unresolvedTrack = await Promise.all(this.buildUnresolved(track));
110
+ return this.buildResponse('TRACK_LOADED', [unresolvedTrack]);
111
+ } catch (e) {
112
+ return this.buildResponse(
113
+ 'LOAD_FAILED',
114
+ [],
115
+ undefined,
116
+ e.body?.error.message ?? e.message,
117
+ );
118
+
119
+
120
+ }
121
+ }
122
+
123
+ async fetchArtist(id) {
124
+
125
+ try {
126
+ const artist = await this.requestData(`/artist/${id}/top`);
127
+ await this.fetchArtistTracks(artist)
128
+
129
+ const limitedTracks = this.options.artistLimit
130
+ ? artist.data.slice(0, this.options.artistLimit * 100)
131
+ : artist.data;
132
+
133
+ const unresolvedArtistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x)
134
+ ));
135
+
136
+
137
+
138
+ return this.buildResponse('PLAYLIST_LOADED', unresolvedArtistTracks, artist.name);
139
+ } catch (e) {
140
+ return this.buildResponse(
141
+ 'LOAD_FAILED',
142
+ [],
143
+ undefined,
144
+ e.body?.error.message ?? e.message,
145
+ );
146
+
147
+
148
+ }
149
+ }
150
+
151
+ async fetchArtistTracks(deezerArtist) {
152
+ let nextPage = deezerArtist.next;
153
+ let pageLoaded = 1;
154
+ while (nextPage) {
155
+ if (!nextPage) break;
156
+ const req = await fetch(nextPage)
157
+ const json = await req.json()
158
+
159
+ deezerArtist.data.push(...json.data);
160
+
161
+ nextPage = json.next;
162
+ pageLoaded++;
163
+ }
164
+ }
165
+
166
+ async fetch(query) {
167
+ if (this.check(query)) return this.resolve(query);
168
+
169
+ try {
170
+ if (this.check(query)) return this.resolve(query)
171
+ let tracks = await this.requestData(`/search?q="${query}"`)
172
+
173
+ const unresolvedTrack = await this.buildUnresolved(tracks.data[0]);
174
+ return this.buildResponse('TRACK_LOADED', [unresolvedTrack]);
175
+ } catch (e) {
176
+ return this.buildResponse(
177
+ 'LOAD_FAILED',
178
+ [],
179
+ undefined,
180
+ e.body?.error.message ?? e.message,
181
+ );
182
+
183
+
184
+ }
185
+ }
186
+
187
+
188
+ async buildUnresolved(track) {
189
+ if (!track) throw new ReferenceError('The Deezer track object was not provided');
190
+
191
+ return new PoruTrack({
192
+ track: '',
193
+ info: {
194
+ sourceName: 'deezer',
195
+ identifier: track.id,
196
+ isSeekable: true,
197
+ author: track.artist ? track.artist.name : 'Unknown',
198
+ length: track.duration,
199
+ isStream: false,
200
+ title: track.title,
201
+ uri: track.link,
202
+ image: track.album.cover_medium
203
+ },
204
+ });
205
+ }
206
+
207
+
208
+ compareValue(value) {
209
+ return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined';
210
+ }
211
+
212
+ buildResponse(loadType, tracks, playlistName, exceptionMsg) {
213
+ return Object.assign(
214
+ {
215
+ loadType,
216
+ tracks,
217
+ playlistInfo: playlistName ? { name: playlistName } : {},
218
+ },
219
+ exceptionMsg ? { exception: { message: exceptionMsg, severity: 'COMMON' } } : {},
220
+ );
221
+ }
222
+
223
+
224
+
225
+
226
+ }
227
+
228
+ module.exports = Deezer;
229
+
230
+ let deezer = new Deezer("", { deezer: { playlistLimit: 10 } })
231
+
232
+
233
+ deezer.resolve("https://www.deezer.com/en/playlist/4404579662")