'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.simpleText, }))); }); }); }; 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;