Compare commits
No commits in common. "0d498bd8afe53cfce3d2ccc7a96d43c8ee49b63f" and "d33770cb4e218bce7d529859d0233bac9623c2d1" have entirely different histories.
0d498bd8af
...
d33770cb4e
|
@ -1,7 +1,7 @@
|
|||
const { InputManager } = require ('./input_manager')
|
||||
const { ResourceManager } = require('./resource_manager')
|
||||
const { PlaylistsManager } = require('./playlists_manager')
|
||||
const { PlayerManager } = require('./player_manager')
|
||||
const { Player } = require('./player')
|
||||
const config = require('../config.json')
|
||||
|
||||
class GuildsManager {
|
||||
|
@ -26,7 +26,7 @@ class GuildsManager {
|
|||
const current_config = config[message.guild.id] || config['default']
|
||||
this.#guilds[guild_id] = {
|
||||
input: new InputManager(this, current_config),
|
||||
player: new PlayerManager(this),
|
||||
player: new Player(this),
|
||||
resource: new ResourceManager(this),
|
||||
playlists: await new PlaylistsManager(this),
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ const LOOP_NAMES = {
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
class PlayerManager {
|
||||
class Player {
|
||||
#voice
|
||||
#dispatcher
|
||||
#playlist
|
||||
|
@ -174,4 +174,4 @@ class PlayerManager {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = { PlayerManager }
|
||||
module.exports = { Player }
|
|
@ -119,10 +119,6 @@ class Playlist {
|
|||
return this.#tracks.length === 0
|
||||
}
|
||||
|
||||
get_length() {
|
||||
return this.#tracks.length
|
||||
}
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @returns {string} a string representation of the playlist
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const fetch = require('node-fetch')
|
||||
const ytdl = require('ytdl-core')
|
||||
const { GuildAction } = require('./guild_action')
|
||||
const { Track } = require('./track')
|
||||
const { youtube_instance } = require("./youtube")
|
||||
|
||||
/**
|
||||
* @class
|
||||
|
@ -14,13 +15,40 @@ class ResourceManager {
|
|||
this.#awaiting_answers = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} search The search pattern that will be sent to Youtube
|
||||
* @return {Track[]} An array of objects representing the search results. <code>source</code> is set to <code>"youtube"</code>
|
||||
*/
|
||||
async #search_youtube(search) {
|
||||
try {
|
||||
const req = await fetch('https://www.youtube.com/results?search_query='
|
||||
+ encodeURIComponent(search))
|
||||
const body = await req.text()
|
||||
const json = JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
|
||||
const results = json.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0].itemSectionRenderer.contents
|
||||
return results
|
||||
.filter(e => e.videoRenderer)
|
||||
.map(e => {
|
||||
return new Track({
|
||||
url: 'https://www.youtube.com/watch?v=' + e.videoRenderer.videoId,
|
||||
title: e.videoRenderer.title.runs[0].text,
|
||||
length: e.videoRenderer.lengthText.simpleText,
|
||||
source: 'youtube'
|
||||
})
|
||||
})
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a research and send the search results back to the Discord channel it's been asked for
|
||||
* @param {ActionParams}
|
||||
* @return {Track[]} An array of objects representing the search results.
|
||||
*/
|
||||
async search({ message, params }) {
|
||||
const tracks = await youtube_instance.search_track(params.join(' '))
|
||||
const tracks = await this.#search_youtube(params.join(' '))
|
||||
if (tracks.length === 0) {
|
||||
message.channel.send(':warning: Something went wrong. Please try again')
|
||||
} else {
|
||||
|
@ -72,11 +100,26 @@ class ResourceManager {
|
|||
}
|
||||
|
||||
async quick_search({ message, params }) {
|
||||
const tracks = await youtube_instance.search_track(params.join(' '))
|
||||
const tracks = await this.#search_youtube(params.join(' '))
|
||||
const action = new GuildAction({ message, params: [tracks[0].get_url()] })
|
||||
this.#gmanager.get_manager(message.guild.id, 'player').add(action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the metadata of a YouTube video
|
||||
* @param {string} url
|
||||
* @returns {Track}
|
||||
*/
|
||||
async #get_youtube_metadata(url) {
|
||||
const video_infos = (await ytdl.getBasicInfo(url)).videoDetails
|
||||
return new Track({
|
||||
url: video_infos.video_url,
|
||||
title: video_infos.title,
|
||||
length: parseInt(video_infos.lengthSeconds),
|
||||
source: 'youtube'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the url of a song, get is metadata. <br />
|
||||
* Note that it only works for YouTube videos for now.
|
||||
|
@ -84,7 +127,7 @@ class ResourceManager {
|
|||
* @returns {Track}
|
||||
*/
|
||||
async get_metadata(url) {
|
||||
return youtube_instance.get_metadata(url)
|
||||
return this.#get_youtube_metadata(url)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
const fetch = require("node-fetch")
|
||||
const { Track } = require("./track")
|
||||
const { Playlist } = require("./playlist")
|
||||
const ytdl = require('ytdl-core')
|
||||
|
||||
/**
|
||||
* Centralize all youtube requests within a single interface.
|
||||
* It allows searching infos (tracks, playlists, metadata) over youtube.
|
||||
*/
|
||||
class Youtube {
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* @param {string} search The search pattern that will be sent to Youtube
|
||||
* @return {Track[]} An array of objects representing the search results. <code>source</code> is set to <code>"youtube"</code>
|
||||
*/
|
||||
async search_track(search) {
|
||||
try {
|
||||
const req = await fetch('https://www.youtube.com/results?search_query='
|
||||
+ encodeURIComponent(search))
|
||||
const body = await req.text()
|
||||
const json = JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
|
||||
const results = json.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0].itemSectionRenderer.contents
|
||||
return results
|
||||
.filter(e => e.videoRenderer)
|
||||
.map(e => {
|
||||
return new Track({
|
||||
url: 'https://www.youtube.com/watch?v=' + e.videoRenderer.videoId,
|
||||
title: e.videoRenderer.title.runs[0].text,
|
||||
length: e.videoRenderer.lengthText.simpleText,
|
||||
source: 'youtube',
|
||||
})
|
||||
})
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all tracks of the playlist on youtube
|
||||
* @param {string} playlist_query an url or id of the youtube playlist. It will recognize the list=xxx pattern.
|
||||
* @returns {Playlist}
|
||||
*/
|
||||
async get_playlist(playlist_query) {
|
||||
const playlist_id = playlist_query.match(/list=(\w+)/)?.[1] || playlist
|
||||
const req = await fetch(`https://www.youtube.com/playlist?list=${playlist_id}`)
|
||||
const body = await req.text()
|
||||
const json = JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
|
||||
// console.log(JSON.stringify(json, null, 2))
|
||||
const results = json.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].playlistVideoListRenderer.contents
|
||||
|
||||
const tracks = results
|
||||
.filter(video => video.playlistVideoRenderer.lengthText)
|
||||
.map((video) => new Track({
|
||||
title: video.playlistVideoRenderer.title.runs[0].text,
|
||||
url: 'https://www.youtube.com/watch?v=' + video.playlistVideoRenderer.videoId,
|
||||
length: video.playlistVideoRenderer.lengthText.simpleText,
|
||||
source: 'youtube',
|
||||
}))
|
||||
return new Playlist({ tracks })
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the metadata of a YouTube video
|
||||
* @param {string} url
|
||||
* @returns {Track}
|
||||
*/
|
||||
async get_metadata(url) {
|
||||
const video_infos = (await ytdl.getBasicInfo(url)).videoDetails
|
||||
return new Track({
|
||||
url: video_infos.video_url,
|
||||
title: video_infos.title,
|
||||
length: parseInt(video_infos.lengthSeconds),
|
||||
source: 'youtube'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const youtube_instance = new Youtube()
|
||||
|
||||
module.exports = {
|
||||
Youtube,
|
||||
youtube_instance,
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
const { Youtube } = require('../src/youtube');
|
||||
|
||||
describe('playlist basic manipulations', () => {
|
||||
const youtube = new Youtube();
|
||||
test('search_track', async () => {
|
||||
const tracks = await youtube.search_track("FROOT: Full Instrumental Album (MARINA AND THE DIAMONDS)")
|
||||
expect(tracks.length).not.toEqual(0)
|
||||
expect(tracks[0].get_url()).toEqual('https://www.youtube.com/watch?v=0WvTtIfX04c')
|
||||
expect(tracks[0].get_title()).toEqual('FROOT: Full Instrumental Album (MARINA AND THE DIAMONDS)')
|
||||
expect(tracks[0].get_length_as_string()).toEqual('48:27')
|
||||
expect(tracks[0].get_source()).toEqual('youtube')
|
||||
})
|
||||
|
||||
test('get_playlist', async () => {
|
||||
const playlist = await youtube.get_playlist("https://www.youtube.com/playlist?list=PLtKVsbX6mq9RvO__n08wujegVI0NsuNiu")
|
||||
const track = playlist.get_current_track()
|
||||
expect(playlist.get_length()).toEqual(35)
|
||||
expect(track.get_url()).toEqual('https://www.youtube.com/watch?v=jfAbX-Fg9N0')
|
||||
expect(track.get_title()).toEqual('Attack on Titan: Original Soundtrack I - attack ON titan | High Quality | Hiroyuki Sawano')
|
||||
expect(track.get_length_as_string()).toEqual('4:21')
|
||||
expect(track.get_source()).toEqual('youtube')
|
||||
})
|
||||
|
||||
test('get_metadata', async () => {
|
||||
const track = await youtube.get_metadata("https://www.youtube.com/watch?v=0WvTtIfX04c")
|
||||
expect(track.get_url()).toEqual('https://www.youtube.com/watch?v=0WvTtIfX04c')
|
||||
expect(track.get_title()).toEqual('FROOT: Full Instrumental Album (MARINA AND THE DIAMONDS)')
|
||||
expect(track.get_length_as_string()).toEqual('48:27')
|
||||
expect(track.get_source()).toEqual('youtube')
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue