Compare commits

...

4 Commits

Author SHA1 Message Date
Thibaut Broggi f52968b2c2
Rename ONE_MS to ONE_SECOND macro 2021-02-26 11:08:58 +01:00
Thibaut Broggi 0245ab275c
Merge branch 'master' of gitlab.com:Emeraude/music-very-player 2021-02-26 10:10:11 +01:00
Thibaut Broggi f1f46267d8
Factorize requests to YouTube 2021-02-26 10:09:20 +01:00
Arthur POULET 8057d3fa7a Add some more documentation 2021-02-26 01:59:11 +01:00
3 changed files with 75 additions and 41 deletions

View File

@ -4,6 +4,14 @@ const { PlaylistsManager } = require('./playlists_manager')
const { PlayerManager } = require('./player_manager')
const config = require('../config.json')
/**
* This class handles each guilds independantly.
* In order to do so, it accept every message in {@link handle_message}
* and redirect the action to execute to other Manager dedicated to only one
* {@link Discord.Guild}.
*
* @class
*/
class GuildsManager {
#guilds
@ -20,6 +28,10 @@ class GuildsManager {
return this.#guilds[guild][name];
}
/**
*
* @param {Discord.Message} message
*/
async handle_message(message) {
const guild_id = message.guild.id
if (!this.#guilds[guild_id]) {

View File

@ -1,6 +1,8 @@
const { Playlist } = require('./playlist')
const { GuildAction } = require('./guild_action')
const ONE_SECOND = 1000
/**
* @typedef {Object} Trigger
* @property {RegExp} pattern The pattern that will trigger the trigger
@ -13,6 +15,13 @@ const { GuildAction } = require('./guild_action')
*/
/**
* InputManager handle message in order to defines which other manager should
* handle it. It uses to do so a mix { target, action } which can be used by
* the GuildsManager.
*
* It also is able to generate Trigger and execute them when some conditions
* are met.
*
* @class
*/
class InputManager {
@ -26,6 +35,12 @@ class InputManager {
this.#triggers = []
}
/**
* Generate a {@link GuildAction} which describe a route to a class (Manager) and an action.
*
* @param {Discord.Message} message discord triggers the current event based on this message
* @returns {GuildAction}
*/
async handle_message(message) {
if (message.content.slice(0, this.#config.prefix.length) === this.#config.prefix) {
const [command, ...command_params] = message.content.slice(this.#config.prefix.length).trim().split(/\s+/)
@ -48,9 +63,9 @@ class InputManager {
this.#triggers = this.#triggers.filter(Boolean)
}
const params = message.content
.split(trigger_target_action.pattern)[1]
.trim()
.split(/\s+/)
.split(trigger_target_action.pattern)[1]
.trim()
.split(/\s+/)
return new GuildAction({ ...trigger_target_action, message, params: params })
}
return null
@ -77,7 +92,7 @@ class InputManager {
if (idx !== -1 && typeof trigger.on_expire === 'function') {
trigger.on_expire(trigger)
}
}, ttl * 1000)
}, ttl * ONE_SECOND)
}
}

View File

@ -9,58 +9,65 @@ const ytdl = require('ytdl-core')
*/
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) {
const req = await fetch('https://www.youtube.com/results?search_query='
+ encodeURIComponent(search))
async #get_json_from_url(url) {
const req = await fetch(url)
const body = await req.text()
const json = JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
return JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
}
async #get_json_from_route(route) {
return await this.#get_json_from_url('https://www.youtube.com/' + route)
}
/**
* @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) {
const json = await this.#get_json_from_route('results?search_query='
+ encodeURIComponent(search))
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',
.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',
})
})
})
}
/**
* 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}
*/
* 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_query
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])
const json = await this.#get_json_from_route('playlist?list='
+ encodeURIComponent(playlist_id))
// 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',
}))
.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}
*/
* 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({