music-very-player/src/youtube.js

88 lines
3.2 KiB
JavaScript

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() {}
async #get_json_from_url(url) {
const req = await fetch(url, {'headers': {'Cookie': 'CONSENT=YES+cb.20210622-13-p0.fr+FX+073'}})
const body = await req.text()
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 || null,
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}
*/
async get_playlist(playlist_query) {
const playlist_id = playlist_query.match(/list=([\w-]+)/)?.[1] || playlist_query
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 || null,
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,
}