110 lines
3.2 KiB
JavaScript
110 lines
3.2 KiB
JavaScript
|
'use strict';
|
|||
|
|
|||
|
const util = require('util');
|
|||
|
const request = require('request');
|
|||
|
const cheerio = require('cheerio');
|
|||
|
|
|||
|
const YOUTUBE_BASE = 'https://www.youtube.com/%s';
|
|||
|
const VIDEO_ID_REGEX = /watch\?v=([^&]*)/i;
|
|||
|
const END_SCREEN_BASE = util.format(YOUTUBE_BASE, 'get_endscreen?v=%s');
|
|||
|
const CARD_BASE = util.format(YOUTUBE_BASE, 'annotations_invideo?video_id=%s');
|
|||
|
|
|||
|
/**
|
|||
|
* Récupère une liste des vidéos liées à la fin d’une autre vidéo.
|
|||
|
*
|
|||
|
* @param videoId Identifiant de la vidéo source.
|
|||
|
* @return Liste des vidéos liées. Chaque vidéo est un objet contenant
|
|||
|
* son identifiant (videoId) et son titre (title).
|
|||
|
*/
|
|||
|
const getEndScreenVideos = videoId =>
|
|||
|
{
|
|||
|
const url = util.format(END_SCREEN_BASE, videoId);
|
|||
|
|
|||
|
return new Promise((resolve, reject) =>
|
|||
|
{
|
|||
|
request(url, (err, res, body) =>
|
|||
|
{
|
|||
|
if (err)
|
|||
|
{
|
|||
|
reject(err);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Suppression des caractères initiaux inutiles si applicable
|
|||
|
if (body.substr(0, 3) === ')]}')
|
|||
|
{
|
|||
|
body = body.substr(3);
|
|||
|
}
|
|||
|
|
|||
|
// Interprétation du JSON
|
|||
|
const data = JSON.parse(body);
|
|||
|
|
|||
|
// Aucun écran de fin
|
|||
|
if (
|
|||
|
typeof data !== 'object' || data === null ||
|
|||
|
data.elements === undefined
|
|||
|
)
|
|||
|
{
|
|||
|
resolve([]);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Filtrage des données pour extraire le titre et l’ID des vidéos
|
|||
|
resolve(data.elements.map(elt => ({
|
|||
|
videoId: elt.endscreenElementRenderer.endpoint
|
|||
|
.watchEndpoint.videoId,
|
|||
|
title: elt.endscreenElementRenderer.title.runs[0].text,
|
|||
|
})));
|
|||
|
});
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
exports.getEndScreenVideos = getEndScreenVideos;
|
|||
|
|
|||
|
/**
|
|||
|
* Récupère une liste des vidéos liées en tant que carte d’une autre vidéo.
|
|||
|
*
|
|||
|
* @param videoId Identifiant de la vidéo source.
|
|||
|
* @return Liste des vidéos liées. Chaque vidéo est un objet contenant
|
|||
|
* son identifiant (videoId) et son titre (title).
|
|||
|
*/
|
|||
|
const getCardVideos = videoId =>
|
|||
|
{
|
|||
|
const url = util.format(CARD_BASE, videoId);
|
|||
|
|
|||
|
return new Promise((resolve, reject) =>
|
|||
|
{
|
|||
|
request(url, (err, res, body) =>
|
|||
|
{
|
|||
|
if (err)
|
|||
|
{
|
|||
|
reject(err);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Interprétation du XML externe et recherche des annotations
|
|||
|
// de type carte
|
|||
|
const nav = cheerio.load(body);
|
|||
|
const cards = nav('annotation[type="card"][style="video"]');
|
|||
|
const list = [];
|
|||
|
|
|||
|
cards.each((i, el) =>
|
|||
|
{
|
|||
|
// Interprétation du JSON de chaque carte, extraction
|
|||
|
// du titre et de l’identifiant de la vidéo
|
|||
|
const data = JSON.parse(nav(el).children('data').text());
|
|||
|
const videoIdResults = VIDEO_ID_REGEX.exec(data.url);
|
|||
|
|
|||
|
list.push({
|
|||
|
title: data.title,
|
|||
|
videoId: videoIdResults ? videoIdResults[1] : null
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
resolve(list);
|
|||
|
});
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
exports.getCardVideos = getCardVideos;
|