J'avais précédemment écrit une petite bibliothèque javascript qui permet de voir les ancres d'une page web, ceci à fins de faire des liens sur le bon chapitre (s'il est indexé) dans un très long article. J'ai ensuite monté Anchors Reveal en add-on pour Firefox, qui se montre redoutablement efficace.

Or, depuis le temps que je monte et héberge le Supplément Week-End — l'un des plus vieux podcast francophone qui s'arrête cette semaine — je regrettais qu'il manque un dispositif équivalent pour un sonore ou une vidéo embarquée dans une page distante. Un truc aussi simple qu'un <a href="page#element">

Il existe néanmoins la possibilité de chapitrer dans certains formats containers, comme le mp4. Mais cette possibilité est très rarement implémentée et le problème reste entier dans le contexte d'une page web.
Ici, une piste marqueur dans le logiciel d'édition Audacity.

Tiens, encore ce matin, dans un e-mail, je voulais pointer la séance des lightning talks du dernier Paris Web les passages de David Rousset et David Catuhe, ben Vimeo ne propose pas de pouvoir pointer à un point temporel précis.

Et comme que des fois, on enchaîne beaucoup d'interviewes dans une émission qui dure deux heures. Et qu'on ne veut pointer que sur une seule à partir d'une autre page. Ouais, il me manquait de pouvoir faire ça :

Dont acte :

Chaînon manquant

Je me suis lancé dans l'écriture d'une petite bibliothèque javascript, à appeler à partir des pages qui seront appelées. L'idée est qu'il suffit de simplement insérer l'appel, et tout se fera tout seul :

<script src="…/timecodehash.js"></script>
Rappel : ne liez jamais ce genre de ressource extérieure à votre site ! Copiez la lib chez vous, elle est GPL !

Le code est du pur javascript, parfum vanilla. Vous n'aurez donc besoin d'aucune autre bibliothèque ou framework tel que jQuery. À une unique exception, à savoir si vous voulez vérifier les tests, le développement a été commencé en TDD. J'ai craqué car il fallait des promesses à QUnit. Oui, je suis faible.

J'ai aussi tenté d'avoir une syntaxe d'URL la plus propre possible, qu'elle n'interfère pas avec les standards et que les ancres soient relativement transparentes. J'ai fait l'impasse sur un fallback, donc en cas de plantage javascript, il ne se passera rien.

Autre chose sur le développement : j'étais aussi en déploiement continu sur mon propre blog. Donc vous ne l'aviez pas vu passé, mais il y tourne depuis le podcast du THSF 2014.

Il y a un standard

Mon script utilise strictement les API de base. Non seulement les éléments natifs sont gérés, mais tout polyfill, script, add-ons ou plugins (ben oui) qui peuvent les prendre en charge sont du coup aussi compatibles.

Il y a peut-être une petite vérification à faire, car oui, il existe encore des vieux tromblons à travers notre vaste mode, que votre serveur web supporte bien les requêtes HTTP Content-range et réponde bien par le status code 206 Partial content. Le chargement par morceaux de ressources n'est d'ailleurs pas toujours supportés par certains reverse-proxy.

Grâce à Thomas Parisot, qui fait moult tours de magie javascript à la BBC, j'ai redécouvert le standard media fragment. Oui, je l'avais lue à une époque et je l'avais stupidement oubliée.
Il permet d'adresser et de n'utiliser qu'une partie d'une ressource média, du type image, audio ou vidéo. Mais il manquait la gestion du lien vers la page où cette ressource est embarquée. C'est pour ça que j'ai construit ce petit bout de code.

Dans les faits, à son implémentation, j'ai parfois une invalidation de cache. Je tente donc d'abord de forcer le lecteur à se positionner au timecode demandé et en cas d'échec, je tente avec la syntaxe que j'utilisais jusqu'ici, à savoir en changeant la propriété element.currentTime.

Comme ces manipulations risquent de lever une erreur, j'ai utilisé non sans honte un try { … } catch(e) { … }. Oui, je sais que cette structure est l'œuvre de Satan, qu'elle n'est pas Bien™ du tout et que ce démon tue les performances, mais je vous fais remarquer qu'on risque pas la dizaine d'appels par seconde pour cette fonction, donc le besoin avoir des perfs de F1 n'est pas ici un facteur de décision.

Autoplay, la sale manie

Rien n'est plus insupportable que les sons/vidéos qui se lancent toutes seules, sans demander d'autorisation. Pourtant ... si vous venez d'une autre page, j'ai considéré que mettre le sonore à une position précise équivalait à une lecture automatique.

Je me demande si c'est raisonnable…

Enfin, là, ça ne marche pas à cause d'un souci d'événement. Assez pénible.

La syntaxe

Elle est très simple : l'URL de la page, suivi du hash de l'élément multimédia (au aucun pour le premier), d'un séparateur (par défaut &t=) et le timecode, exprimé en secondes (123), deux-points séparateur (00:02:03) ou unités (2m3s)

Ce qui donne ceci :

<a href="http://dascritch.net/post/2014/05/31/Suppl%C3%A9ment-Week-End%2C-samedi-31-Mai-2014#&t=2m3s">

Remerciements et TODO list

En plus de Thomas Parisot et des débuggueurs Loïc Gerbaud et Benoît Salles, il me faut aussi remercier l'équipe éditrice de Touchalize, à savoir Phonitive et son créateur Guillaume Lemoine, pour m'avoir prêté de l'équipement afin de faire différents tests. Ce qui m'a permis de vérifier qu'effectivement Safari est vraiment bugué à la moelle capricieux au possible.

Tout n'est pas terminé : il manque encore le support des éléments AV non indexés (je rappelle que c'est pas franchement l'idéal), et des chapitrages déjà existants.

Cadeau !

Petit bonus propre à Firefox : le menu contextuel sur l'élément (clic droit ou appui prolongé sur l'élément média) permet de récupérer le lien vers la position en cours de lecture. Mais comme on n'a plus le droit de modifier le presse-papier comme on veut pour d'évidentes raisons de sécurité, vous aurez alors l'interface roots d'un bon vieux alert() des familles.