@
X
$
=
@

NOUS LES DEVS

Embed Youtube personnalisé

Intégrer LE player vidéo de manière personnalisée, naviguer dans ta playlist sans refresh de page, mettre à jour la durée et la date de publication dynamiquement.

Niveau : intermédiaire
</> </> </>

Youtube

Comme tu dois le savoir, je fais occasionnellement des vidéos pour compléter certains de mes articles. C’est un format hyper intéressant et sans doute le plus puissant pour apprendre rapidement. Je m’en sers surtout pour toutes les démos qui nécessitent un contexte assez long à mettre en place.

Aujourd’hui, je te propose de découvrir comment j’ai réalisé mon module vidéo qui est à la fois utile, fonctionnel et vachement plus graphique que ce qu’on trouve habituellement sur certains sites. Grâce à lui, tu peux naviguer au travers de mes vidéos sans avoir de rafraichissement de page et tu as la durée et la date de publication qui se mettent automatiquement à jour. Dans ce tuto, je vais aborder purement le côté technique de la récupération des vidéos de Youtube et le pilotage de l’embed avec l’API Javascript.

component_youtube

Le squelette

La première étape est de poser la base HTML. En somme, c’est un conteneur qui contient un player, différents boutons ainsi que la boîte avec les informations de la vidéo en cours telles que le temps et la date de sortie de celle-ci. Quant au CSS, la plupart des éléments sont positionnés en absolute par rapport au container. Je te laisse regarder sur ma home page, il n’y a rien que tu ne saches inspecter pour voir comment je m’y suis pris, c’est relativement simple.

<div class="youtubeVideo">
	<div id="player"></div>
	<a href="https://www.youtube.com/channel/UCfZBrtgJ1CCYmhlbJVxSk0A" target="_blank" class="title red">Ma chaîne Youtube</a>
	<div class="bt_navigation previous_video red">
		<div class="shape"></div>
		<span>Vidéo précédente</span>
	</div>
	<div class="bt_navigation next_video red">
		<span>Vidéo suivante</span>
		<div class="shape"></div>
	</div>
	<div class="meta red">
		<div class="duration">
			<span class="label">Durée : </span><span class="value"></span>
		</div>
		<div class="date">
			<span class="label">Date : </span><span class="value"></span>
		</div>
	</div>
</div>

API Google

La deuxième étape est de configurer des crédentials pour pouvoir lancer des requêtes sur l’API de Youtube. C’est sur la console de Google cloud que ça se passe.

Une fois le projet créé, il faudra aller dans « API et services » et ensuite dans « Bibliothèque ».

google_console_api_services

Taper dans la recherche Youtube et choisir « YouTube Data API V3 ».

youtube_data_service_api

Ensuite tu peux l’activer. Une fois que cela est fait, tu devrais obtenir la même chose que moi.

youtube_data_api_service_manage

Reviens sur la page « API et services », clique sur « Identifiants » et crée une clé d’API en cliquant sur le bouton « + CRÉER DES IDENTIFIANTS » et en sélectionnant « Clé API ».

identifiant_cle_api

Ta clé d’API vient de se créer, tu peux la récupérer en cliquant sur l’icône du double carré.

youtube_api_key

Mise en cache des données

Vu que je fais des vidéos de manière occasionnelle, je vais travailler avec un cache. Ca sera beaucoup plus performant et je ne paierai pas le service vu que je lancerai un nombre assez limité de requêtes. L’idée c’est de stocker le résultat dans la base de données et de n’en relancer uniquement que lorsque j’en aurai besoin, c’est-à-dire lorsque je créerai une nouvelle vidéo.

Déclenchement

Je me suis branché sur un hook tout bête qui est la sauvegarde de ma home page, mais ça aurait pu être n’importe quoi d’autre. Le code ci-dessous est du PHP issu de mon youtube.lib.php qui est inclus au niveau du fichier functions de mon thème WordPress.

add_action( 'save_post', 'youtube_get_last_youtube_video' );
function youtube_get_last_youtube_video( $post_id ){
	if( get_option( 'page_on_front' ) == $post_id){

	}
}

Je définis les constantes dont j’aurai besoin dans le programme.

const YOUTUBE_CHANNEL_ID		= 'LE_CHANNEL_ID_ID_DE_TA_CHAINE_YOUTUBE';
const YOUTUBE_API_KEY			= 'TA_CLE_API';
const YOUTUBE_NBR_RESULT		= 99;
const YOUTUBE_META_NAME_TO_SAVE		= 'nld_last_youtube_video_information';

Et à l’intérieur de ma fonction youtube_get_last_youtube_video, je vais réaliser trois étapes ; récupérer les données, les parser et les enregistrer. Je vais les détailler ci-dessous.

Récupération des données

Pour récupérer les données, je fais tout simplement un file_get_content sur l’URL adéquat. Je te laisserai analyser les paramètres et jeter un coup d’œil dans la documentation de Google pour voir ce qu’on peut y récupérer.

function get_last_youtube_video_ids(){
	$endpoint = 'https://www.googleapis.com/youtube/v3/search?part=id,snippet&channelId=' . YOUTUBE_CHANNEL_ID . '&maxResults=' . YOUTUBE_NBR_RESULT . '&order=date&type=video&key=' . YOUTUBE_API_KEY;
	return file_get_contents($endpoint);
}

Parsing des données

Une fois les données récupérées depuis Google, j’appelle une fonction qui va se charger de les parser et de me retourner les ID et la date de publication des vidéos. Dans l’API, je n’ai pas trouvé comment retourner le temps des vidéos sans relancer d’autres requêtes, je traiterai ce point côté JS.

$result 		= get_last_youtube_video_ids();
$data_ids_and_data 	= parse_youtube_result_api( $result);

Dans la fonction de parsing, après un json_decode, je fais un foreach sur l’ensemble des vidéos récupérées et je teste juste l’existence de clés. J’extrais les données dont j’ai besoin, à savoir l’ID de la vidéo et la date de publication que je retourne ensuite dans un tableau.

	function parse_youtube_result_api( $result ){

		$data 			= json_decode($result, TRUE);

		$youtube_video_information = array();

		if( isset($data["items"]) && !empty($data["items"]) ){
			foreach($data["items"] as $item){

				$youtube_id 				= null;
				$youtube_publication_date 	= null;

				if( isset($item["id"]) && isset($item["id"]["videoId"]) && $item["id"]["videoId"] != "" ){
					$youtube_id = sanitize_text_field($item["id"]["videoId"]);
				}
				if( isset($item["snippet"]) && isset($item["snippet"]["publishedAt"]) && $item["snippet"]["publishedAt"] != "" ){
					$date = substr($item["snippet"]["publishedAt"], 0, 10);
					$date = explode("-",$date);
					$youtube_publication_date = $date[2] . "." . $date[1] . "." . $date[0];
				}
				if( $youtube_id != null && $youtube_publication_date != null ){
					$youtube_video_information[] = array(
						$youtube_id,
						$youtube_publication_date
					);
				}


			}
		}

		return $youtube_video_information;
		
	}

Voici le résultat pour te donner une idée de ce que retourne ma fonction.

Array
(
    [0] => Array
        (
            [0] => fo4d0xnoaEg
            [1] => 29.12.2021
        )

    [1] => Array
        (
            [0] => 9t7cx_HIEhs
            [1] => 31.07.2019
        )

    [2] => Array
        (
            [0] => CykKc3W-nz4
            [1] => 25.07.2019
        )
...

Enregistrement des données

L’enregistrement est un update_post_meta directement appelé après l’exécution des deux fonctions ci-dessus. Je t’ai remis la fonction youtube_get_last_youtube_video au complet.

add_action( 'save_post', 'youtube_get_last_youtube_video' );
function youtube_get_last_youtube_video( $post_id ){
	if( get_option( 'page_on_front' ) == $post_id){
		$result 	= get_last_youtube_video_ids();
		$data_ids_and_data 	= parse_youtube_result_api( $result);
		if( !empty($data_ids_and_data) ){
			update_post_meta($post_id, YOUTUBE_META_NAME_TO_SAVE, $data_ids_and_data );
		}
	}
}

Données pour le JS

Une fois que la récupération des données est terminée, il faut les rendre accessibles pour le JavaScript. J’ai passé les informations dans un attribut au niveau de mon composant HTML.

<div class="youtubeVideo" data-videolistinformation="<?php echo htmlspecialchars(json_encode(get_post_meta(get_the_ID(), YOUTUBE_META_NAME_TO_SAVE, true))); ?>">
	<div id="player"></div>

</div>

Chargement API Youtube

Maintenant, on passe côté client (JavaScript) et je commence par charger l’API Youtube.

var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

Préparation des variables

J’extrais les données de mes vidéos qui sont fournies via l’attribut data-videolistinformation de mon composant HTML et je les stocke dans le tableau youtube_video_list. Lorsque l’utilisateur navigue au travers de mes vidéos avec le bouton suivant, je vais avoir besoin de détecter quand il est à la dernière vidéo afin de le faire revenir sur la première. Pour cette raison, j’utilise également la variable video_list_length qui me permet de connaître le nombre total de vidéos. video_list_index est la position de la vidéo courante dans le tableau de mes vidéos.

var youtube_video_list = [];
var video_list_index = 0;
var video_list_length = 0;

function prepare_youtube_video() {
	element = document.querySelector('.youtubeVideo');
	youtube_video_list = JSON.parse(element.getAttribute('data-videolistinformation'));
	video_list_length = youtube_video_list.length;
}

prepare_youtube_video();

Youtube Ready

Lorsque l’iframe API Youtube est chargée, elle va déclencher l’exécution d’une fonction onYouTubeIframeAPIReady. Grâce à elle, je peux construire le player avec la première vidéo de ma playlist.

function onYouTubeIframeAPIReady() {
	player = new YT.Player('player', {
		videoId: youtube_video_list[video_list_index][0],
		host: 'https://www.youtube-nocookie.com',
		events: {
			'onReady': onPlayerReady,
			'onStateChange': onPlayerStateChange
		},
		modestbranding: 1,
		rel: 0
	});
}

Lorsque le player est prêt, la fonction onPlayerReady va être appelée. A ce moment-là, je vais pouvoir mettre à jour l’encart méta de mon composant avec la date mais également avec la durée que je n’avais pas pu récupérer via la requête côté serveur mais qui est accessible via le player.

function onPlayerReady(event) {
	updateVideoInformation();
}
function updateVideoInformation() {
	duration = "";
	totalSeconds = player.getDuration();
	hours = Math.floor(totalSeconds / 3600);
	totalSeconds %= 3600;
	minutes = Math.floor(totalSeconds / 60);
	if (hours < 10) {
		hours = "0" + hours;
	}
	duration = hours + "H" + minutes;

	// replace duration and date
	document.querySelector('.youtubeVideo .meta .duration .value').innerHTML = duration;
	document.querySelector('.youtubeVideo .meta .date .value').innerHTML = youtube_video_list[video_list_index][1];
}

Navigation

Il ne me reste maintenant plus qu’une seule chose à faire, c’est la gestion du clic sur vidéo suivante et vidéo précédente. Pour lancer la vidéo suivante, je vais naturellement utiliser un écouteur sur le bouton prévu à cet effet et créer la fonction associée qui sera appelée au clic.

next_video_el.addEventListener("click", load_next_video);

Si l’utilisateur déclenche l’évènement, la fonction de rappel va incrémenter l’index et la vidéo correspondant à ce nouvel index va être lancée. S’il arrive à la fin de la liste, l’index est réinitialisé à zéro pour le faire revenir à la première vidéo.

function load_next_video() {
	if (video_list_index >= (video_list_length - 1)) {
		video_list_index = 0;
	} else {
		video_list_index++;
	}
	update_video_by_video_list_index();
}

Cette fonction va ré-executer à chaque fois un onPlayerReady qui permettra la mise à jour des métas (la date et la durée) de manière automatique.

function update_video_by_video_list_index() {
	player.loadVideoById(youtube_video_list[video_list_index][0]);
}

Et ici le code pour le clic précédent pour les paresseux.

previous_video_el.addEventListener("click", load_previous_video);
function load_previous_video() {
	if (video_list_index <= 0) {
		video_list_index = video_list_length - 1
	} else {
		video_list_index--;
	}
	update_video_by_video_list_index();
}

Pouce Bleu

Voilà, tu peux maintenant créer ton propre module Youtube personnalisé et te distinguer des autres en proposant quelque chose d’un peu plus graphique et plus intégré. Et comme le disent les Youtubeurs, n’hésite pas à partager et à t’abonner haha.

06/11/2022

Yann Vangampelaere - nouslesdevs -

NOUS LES DEVS

Vous aimez ce que je fais ? Vous voulez que j'en fasse plus ? dans le développement du blog.