Olivier Mengué – Code & rando - Tag - télé2024-03-29T11:29:45+01:00urn:md5:57e646ab8ca85028168daaaa985aa995DotclearTélérama → XMLTV : mise à joururn:md5:844f113c8a48ccb174097b15eb3dd1202008-03-02T01:44:00+00:002013-08-10T16:22:59+00:00Olivier MenguéXMLTVFrancegrabberTVtéléTéléramaXMLTV<p>J'ai mis en ligne une version corrigée des mes outils de transformation de la grille télé de Télérama en XMLTV (voir <a href="http://o.mengue.free.fr/blog/2008/02/11/51-les-programmes-tele-de-telerama-en-xmltv">mon billet précédent</a>). Maintenant l'<a href="http://doc.ubuntu-fr.org/ontv">applet OnTV</a> disponible pour le bureau Gnome fonctionne avec ces outils.</p>
<p style="text-align: center"><img src="http://o.mengue.free.fr/dolmen.blog/public/20080302-capture-ontv.png" alt="Capture de l'affichage des programmes avec OnTV" /></p> <p>J'ai corrigé les fichiers <a href="http://o.mengue.free.fr/xmltv/xmltv.xslt"><code>xmltv.xslt</code></a> et <a href="http://o.mengue.free.fr/xmltv/tv.sh"><code>tv.sh</code></a>.</p>
<p>Pour utiliser ce grabber avec OnTV il vous faut récupérer les fichiers suivants et les placer ensemble dans le répertoire que vous souhaitez :</p>
<ul>
<li><a href="http://o.mengue.free.fr/xmltv/leprogramme.xslt" type="text/xsl"><code>leprogramme.xslt</code></a></li>
<li><a href="http://o.mengue.free.fr/xmltv/chaines.xslt" type="text/xsl"><code>chaines.xslt</code></a></li>
<li><a href="http://o.mengue.free.fr/xmltv/xmltv.xslt" type="text/xsl"><code>xmltv.xslt</code></a></li>
<li><a href="http://o.mengue.free.fr/xmltv/tv.sh" type="application/x-shellscript"><code>tv.sh</code></a></li>
</ul>
<p>Éditez <code>tv.sh</code> pour sélectionner les chaînes qui vous intéressent. Puis donnez les droits d'exécution sur tv.sh et testez que tout fonctionne :</p>
<pre class="terminal"><samp class="prompt">$ </samp><kbd>chmod u+x tv.sh</kbd>
<samp class="prompt">$ </samp><kbd>./tv.sh</kbd>
<samp class="curl"> % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 58925 0 58925 0 0 91714 0 --:--:-- --:--:-- --:--:-- 143k
</samp><samp class="prompt">$ </samp><kbd>gedit tv.xml</kbd>
</pre>
<p>Il suffit ensuite d'ajouter l'applet OnTV à votre tableau de bord et d'indiquer dans ses préférences les paramètres suivants sur l'onglet Général (OnTV 2.8.0) </p>
<ul>
<li><samp>Grabber command: </samp><var>le chemin complet vers tv.sh</var></li>
<li><samp>Output file: </samp><var>le chemin complet vers tv.xml</var></li>
</ul>
<p>Lancez la « mise à jour des programmes », puis retournez sur dans les préférences et, sur l'onglet « Chaînes », cochez dans l'ordre les chaînes qui vous intéressent.</p>
<p>Cliquez sur l'icône de OnTV et le programme devrait être là.</p>Les programmes télé de Télérama en XMLTVurn:md5:847ff4c10a314f8538e43d2889043dbe2008-02-11T00:19:00+00:002011-06-05T21:14:20+00:00Olivier MenguéXMLTVcurlEPGgrille téléHTMLJavaScriptPerlprogramme téléservice webTVtéléTéléramawebdevwebserviceXHTMLXMLXMLTVXSLTxsltproc<p><a href="http://xmltv.org/wiki/" hreflang="en">XMLTV</a> est un format informatique pour les programmes de télévision. Ce format est utilisable dans un nombre grandissant d'applications. Le magazine <a href="http://www.telerama.fr/">Télérama</a> propose sur son site une <a href="http://television.telerama.fr/tele/grille.php">grille des programmes télé</a>, mais juste consultable sur le web. Pas de XMLTV.</p>
<p>Je vous propose donc de combler ce manque avec un petit cours de reverse engineering sur le web et la démonstration de quelques outils pour arriver rapidement à vos fins. La grille de Télérama est une excellente cible pour ce genre d'exercice parce que, vous le verrez, la tâche n'est pas simple, mais possible. Elle donne l'ocassion de montrer l'usage de plusieurs outils (curl, XSLT, Perl) et c'est un exemple de ma démarche de prototypage rapide en utilisant le meilleur de chacun.</p>
<p><strong>MàJ 2008-02-15 :</strong> j'ai profondément remanié l'introduction et la conclusion suite à quelques commentaires de lecteurs.</p> <p style="text-align: center;"><a href="http://o.mengue.free.fr/xmltv/xsltv/tv.html"><img src="http://o.mengue.free.fr/dolmen.blog/public/20080214-grille-telerama-488.jpg" alt="La grille du site Télérama.fr" /></a></p>
<p>Cette grille fonctionne avec des <a href="http://fr.wikipedia.org/wiki/Service_web">services web</a>. Cela se voit en cliquant sur les flèches à gauche ou à droite du bandeau des heures : seule la grille est rechargée, et non pas la page entière. Un service web est par définition exploitable pour récupérer des données structurées, plus légères que la page entière affichée dans le navigateur.</p>
<p>La première étape : un survol rapide du source HTML de la page. On repère les éléments suivants :</p>
<ol>
<li>La page est très grosse : 208 381 octets. Toute la grille initiale est donc fournie en HTML. Ce sont les mises à jour qui sont fournies en service web.</li>
<li>La page est déclarée comme <a href="http://fr.wikipedia.org/wiki/XHTML">XHTML</a> d'après l'entête :
<pre class="code vim vimft-html"><span class="Comment"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"</span>
<span class="Comment"> "<a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</a>"></span>
<span class="Identifier"><</span><span class="Statement">html</span><span class="Identifier"> xmlns=</span><span class="Constant">"<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>"</span><span class="Identifier"> xml:</span><span class="Type">lang</span><span class="Identifier">=</span><span class="Constant">"fr"</span><span class="Type">lang</span><span class="Identifier">=</span><span class="Constant">"fr"</span><span class="Identifier">></span>
</pre></li>
<li>Plusieurs balises <SCRIPT> en majuscules. Or, en XHTML ces balises doivent être en minuscules. Ceci indique déjà que malgré la déclaration le XHTML n'est pas valide et donc ne sera pas utilisable directement dans un analyseur XML.</li>
<li>Beaucoup de références à des fichiers JavaScript additionnels. Les familiers d'<a href="http://www.ajaxian.com/">Ajaxian.com</a> détectent vite lesquels sont des librairies JavaScript bien connues. Pour les autres une petite recherche sur Google sur le nom du fichier donne rapidement la réponse.</li>
<li>Un bloc de JavaScript révélant l'adresse du service web et le framework utilisé pour l'appeler :
<pre class="code vim vimft-html"><span class="Identifier"><</span><span class="Statement">script</span><span class="Type">type</span><span class="Identifier">=</span><span class="Constant">"text/javascript"</span><span class="Identifier">></span>
<span class="Identifier">var</span><span class="Special"> xajaxRequestUri=</span><span class="Constant">"<a href="http://television.telerama.fr/tele/grille.php">http://television.telerama.fr/tele/grille.php</a>"</span><span class="Special">;</span>
<span class="Identifier">var</span><span class="Special"> xajaxDebug=</span><span class="Constant">false</span><span class="Special">;</span>
<span class="Identifier">var</span><span class="Special"> xajaxStatusMessages=</span><span class="Constant">true</span><span class="Special">;</span>
<span class="Identifier">var</span><span class="Special"> xajaxWaitCursor=</span><span class="Constant">true</span><span class="Special">;</span>
<span class="Identifier">var</span><span class="Special"> xajaxDefinedGet=</span>0<span class="Special">;</span>
<span class="Identifier">var</span><span class="Special"> xajaxDefinedPost=</span>1<span class="Special">;</span>
<span class="Identifier">function</span><span class="Special"> xajax_chargerProgramme</span>()<span class="Identifier">{</span><span class="Statement">return</span><span class="Special"> xajax.call</span>(<span class="Constant">"chargerProgramme"</span><span class="Special">, </span><span class="Identifier">arguments</span><span class="Special">, </span>1)<span class="Special">;</span><span class="Identifier">}</span>
<span class="Identifier"></</span><span class="Statement">script</span><span class="Identifier">></span>
</pre></li>
<li>Des champs de formulaire tel que celui-ci :
<pre class="code vim vimft-html"><span class="Identifier"><</span><span class="Statement">select</span><span class="Type">style</span><span class="Identifier">=</span><span class="Constant">"width: 120px;"</span><span class="Special">onchange="</span><span class="Special">ajouterChaine</span>(<span class="Identifier">this</span><span class="Special">.options</span><span class="Identifier">[</span><span class="Identifier">this</span><span class="Special">.options.selectedIndex</span><span class="Identifier">]</span><span class="Special">.value</span>)<span class="Special">;</span><span class="Special">"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">option</span><span class="Type">value</span><span class="Identifier">=</span><span class="Constant">""</span><span class="Identifier">></span>Ajoutez une cha<span class="Special">&icirc;</span>ne<span class="Identifier"></</span><span class="Statement">option</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">OPTGROUP</span><span class="Type">label</span><span class="Identifier">=</span><span class="Constant">"Groupes de cha</span><span class="Special">&icirc;</span><span class="Constant">nes"</span><span class="Identifier">/></span>
<span class="Identifier"><</span><span class="Statement">option</span><span class="Type">value</span><span class="Identifier">=</span><span class="Constant">"192,4,80,34,47,111,118"</span><span class="Identifier">></span>Chaînes hertziennes<span class="Identifier"></</span><span class="Statement">option</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">option</span><span class="Type">value</span><span class="Identifier">=</span><span class="Constant">"192,4,80,34,47,111,118,445,119,195,446,444,78,234,481,226,458,482"</span><span class="Identifier">></span>Chaînes TNT<span class="Identifier"></</span><span class="Statement">option</span><span class="Identifier">></span>
</pre>
On voit qu'il s'agit d'une liste de codes pour les différentes chaînes de télé à afficher.
</li>
<li>Des appels de fonction JavaScript :
<pre class="code vim vimft-html"><span class="Identifier"><</span><span class="Statement">a</span><span class="Type">href</span><span class="Identifier">=</span><span class="Constant">"javascript:void(0);"</span><span class="Special">onclick="</span><span class="Statement">return</span><span class="Special"> changerJour</span>(0)<span class="Special">;</span><span class="Special">"</span><span class="Identifier">></span><span class="Underlined">Aujourd'hui</span><span class="Identifier"></</span><span class="Statement">a</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">a</span><span class="Type">href</span><span class="Identifier">=</span><span class="Constant">"javascript:void(0);"</span><span class="Special">onclick="</span><span class="Statement">return</span><span class="Special"> changerMaintenant</span>(0)<span class="Special">;</span><span class="Special">"</span><span class="Identifier">></span><span class="Underlined">Maintenant</span><span class="Identifier"></</span><span class="Statement">a</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">a</span><span class="Type">href</span><span class="Identifier">=</span><span class="Constant">"javascript:void(0);"</span><span class="Special">onclick="</span><span class="Statement">return</span><span class="Special"> changerCeSoir</span>(0)<span class="Special">;</span><span class="Special">"</span><span class="Identifier">></span><span class="Underlined">Ce soir</span><span class="Identifier"></</span><span class="Statement">a</span><span class="Identifier">></span>
</pre>
On note ainsi quelques noms de fonctions qu'on pourra chercher plus tard.</li>
<li>Le code HTML correspondant aux éléments de la grille. Par exemple :
<pre class="code vim vimft-html"><span class="Identifier"><</span><span class="Statement">div</span><span class="Type">class</span><span class="Identifier">=</span><span class="Constant">"emission magazine"</span><span class="Type">style</span><span class="Identifier">=</span><span class="Constant">"width:16px;left:677px;background-image: url('<a href="http://icon-telerama.sdv.fr/iconsv2/bg_grille_genre_vide.gif">http://icon-telerama.sdv.fr/iconsv2/bg_grille_genre_vide.gif</a>');height:51px; cursor: pointer; z-index: 10;"</span><span class="Type">id</span><span class="Identifier">=</span><span class="Constant">"emission_8069449"</span><span class="Special">onclick="</span><span class="Statement">return</span><span class="Special"> afficherEmission</span>(<span class="Constant">'8069449'</span><span class="Special">, </span><span class="Constant">'226'</span>)<span class="Special">;</span><span class="Special">"</span><span class="Type">alt</span><span class="Identifier">=</span><span class="Constant">"Journal de la nuit - Vendredi 08 février de 02h30 à 02h34"</span><span class="Type">title</span><span class="Identifier">=</span><span class="Constant">"Journal de la nuit - Vendredi 08 février de 02h30 à 02h34"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">div</span><span class="Type">class</span><span class="Identifier">=</span><span class="Constant">"conteneur"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">span</span><span class="Type">class</span><span class="Identifier">=</span><span class="Constant">"genre"</span><span class="Identifier">></span><span class="Identifier"></</span><span class="Statement">span</span><span class="Identifier">></span><span class="Identifier"><</span><span class="Statement">br</span><span class="Identifier"> /></span>
<span class="Identifier"><</span><span class="Statement">span</span><span class="Type">class</span><span class="Identifier">=</span><span class="Constant">"titre"</span><span class="Identifier">></span>...<span class="Identifier"></</span><span class="Statement">span</span><span class="Identifier">></span><span class="Identifier"><</span><span class="Statement">br</span><span class="Identifier"> /></span>
<span class="Identifier"><</span><span class="Statement">span</span><span class="Type">style</span><span class="Identifier">=</span><span class="Constant">"resume"</span><span class="Identifier">></span><span class="Identifier"></</span><span class="Statement">span</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Statement">div</span><span class="Type">id</span><span class="Identifier">=</span><span class="Constant">"data_8069449"</span><span class="Type">style</span><span class="Identifier">=</span><span class="Constant">"display:none;"</span><span class="Identifier">></span>{"Id_Diffusion":"8069449","Id_Emission":"9648923","Id_Chaine":"226","Date_Debut":"2008-02-08 02:30:00","Date_Fin":"2008-02-08 02:34:00","Titre":"Journal de la nuit","Sous_Titre":"","ShowViewFr":"46271175","note_T":"0","Id_Rubrique":"8108","Rubrique_Libelle":"Journal","Rubrique_Niveau":"3","Type":"Magazine","Chaine_Nom":"i Télé","Logo":"226.gif","Id_Hierarchie":"040801","DureeEnSecondes":"240","resume_court":"","resume_long":"","dateheurechaine":"Vendredi 08 février de 02h30 à 02h34 sur i Télé","intervenant":""}<span class="Identifier"></</span><span class="Statement">div</span><span class="Identifier">></span>
<span class="Identifier"></</span><span class="Statement">div</span><span class="Identifier">></span>
</pre>
On remarque en particulier l'élement <code>data_8069449</code> qui n'est pas affiché et qui ressemble à des données au format <a href="http://fr.wikipedia.org/wiki/JSON">JSON</a>.
</li>
</ol>
<p>L'étape suivante consiste à récupérer une copie de la page et de tous les scripts additionnels, feuilles de style et images. Il suffit dans Firefox de choisir le menu Fichier → Enregistrer sous... → Page complète.</p>
<p>On obtient les fichiers suivants :</p>
<pre class="terminal"><samp class="prompt">$ </samp><kbd>ls</kbd>
<samp>grille.php.html grille.php_fichiers/</samp>
<samp class="prompt">$ </samp><kbd>ls grille.php_fichiers/</kbd>
<samp>1520033938Top1Bottom2x01x10.js mochikit_packed_tele_min.js
AC_RunActiveContent_min.js notes.js
ajax-loader.gif oas.js
alerte.js ong_montel_off.gif
builder.js ong_podcast_off.gif
carrousel_min.js ong_telerama_on.gif
controls.js ong_wiz_off.gif
couverture.gif pied.js
dragdrop.js prototype.js
effects.js rss.gif
empty.gif scriptaculous.js
favoris.js scriptaculous_packed_tele_min.js
fleche_droite_bottom1.gif scripts_2007.js
fleche_gauche_bottom1.gif slider.js
grille_croix.gif sound.js
grille_heures_fleche_dro.gif styles_2007.css
grille_heures_fleche_gau.gif T1_p.gif
heureFloatEntete.js T2_p.gif
hit.gif T3_p.gif
horsserie.js T5_p.gif
js_tra_authentification.html trans.gif
M5447.jpg tra_tele_v2.js
M5456.jpg urchin.js
M5495.jpg xajax.js
menu.js xtroi.js
milieu.js</samp>
<samp class="prompt">$ </samp><kbd>du -b -c grille.php.html grille.php_fichiers/</kbd>
<samp>210394 grille.php.html
546599 grille.php_fichiers/
756993 total</samp></pre>
<p>On peut alors les trier en trois catégories :</p>
<ol>
<li>les librairies : Scriptaculous, Prototype, Xajax, MochiKit. Repérer les librairies permet d'aller sur le web chercher la doc des fonctions et des objets et d'éviter de plonger le nez dans du JavaScript compressé.</li>
<li>les pubs et les pisteurs : on les reconnaît au faible nombre de lignes et à la piètre qualité du code écrit une fois il y a dix ans et jamais mis à jour en fonction des nouvelles normes (XHTML, DOM). Exemple : <a href="view-source:http://statique-telerama.sdv.fr/scripts/oas.js">oas.js</a></li>
<li>les scripts spécifiques au site : non compressé, avec un nom de fichier, des commentaires ou des noms de fonctions en français, ou contenant des noms de fonctions repérés dans des gestionnaires d'évènements attachés à un tag HTML (par exemple, <code>changerMaintenant()</code> vu plus haut). Chercher le nom du site (<code>grep telerama *.js</code>) permet aussi de les isoler. Ce sont bien sûr les plus importants puisque c'est là que l'on trouve les adresses des services web et la manière de les appeler.</li>
</ol>
<p>Ce premier aperçu permet de se donner une idée de la compétence du/des développeurs. En l'occurence :</p>
<ul>
<li>Malgré la déclaration de l'entête, <a href="http://validator.w3.org/check?verbose=1&uri=http%3A%2F%2Ftelevision.telerama.fr%2Ftele%2Fgrille.php">la page n'est pas du XHTML valide</a>. La validation avec <a href="http://validator.w3.org/">l'outil du W3C</a> révèle ainsi 384 erreurs !</li>
<li>Le fichier HTML est énorme (200 Ko), le nombre de fichiers également. Le poids total de la page est de plus de 800 Ko ! On a beau être à l'heure de l'ADSL en France, ce n'est pas une raison.</li>
<li>De nombreuses librairies JavaScript sont utilisées. Leurs fonctions se recouvrent et sont redondantes. Il existe au moins 3 façons différentes de faire un appel AJAX. Est-ce que plusieurs développeurs ont travaillé sur cette page successivement ? En tout cas le manque de coordination est flagrant.</li>
<li>Les tags XHTML sont détournés pour stocker des données JSON contenant elles-même du HTML (et non du XHTML). Pas étonnant que cela ne valide pas !</li>
<li>Beaucoup de code mort laissé en commentaires dans le JavaScript.</li>
</ul>
<p>En somme, pas brillant.</p>
<p>Nous avons repéré plus haut l'adresse d'un service web. Il est temps de le tester, en dehors du navigateur pour examiner les données retournées. Ce service est appellé avec <a href="http://xajaxproject.org/">Xajax</a>. En fouinant dans xajax.js, on trouve vite (cherchez "postData") la façon dont Xajax envoie les données au serveur. On peut alors reconstruire une requête avec <a href="http://curl.hacxx.se/">curl</a> :</p>
<pre class="terminal"><samp class="prompt">$ </samp><kbd>curl --data "xajax=chargerProgramme&xajaxr=$(date +%s)&xajaxargs[]=$(date '+%Y-%m-%d %H:00:00')&xajaxargs[]=192,4,80,34,47,111,118" http://television.telerama.fr/tele/grille.php</kbd>
</pre>
<p>On obtient cette fois du beau XML valide dont voici la structure :</p>
<pre class="code vim vimft-xml"><span class="Identifier"><</span><span class="Identifier">xjx</span><span class="Identifier">><</span><span class="Identifier">cmd</span><span class="Type">n</span>=<span class="Constant">"as"</span><span class="Type">t</span>=<span class="Constant">"leprogramme"</span><span class="Type">p</span>=<span class="Constant">"innerHTML"</span><span class="Identifier">></span><span class="Type"><![</span><span class="Statement">CDATA</span><span class="Type">[</span><var>...</var><span class="Type">]]></span><span class="Identifier"></cmd></span><var>...</var><span class="Identifier"></xjx></span>
</pre>
<p>Le contenu de <code>leprogramme</code> est le même que ce que l'on trouve dans la page HTML de base. On retrouve notamment la description des programmes à la fois en HTML et en JSON. Ce n'est pas vraiment efficace en terme de quantité de données transférées sur le réseau. Les données JSON aurait été suffisantes, et je l'avoue, cela m'aurait facilité la tâche.</p>
<p>Il suffit d'appliquer le filtre XSLT suivant (<a href="http://o.mengue.free.fr/xmltv/leprogramme.xslt" type="text/xsl">leprogramme.xslt</a>) pour extraire le contenu de <code>leprogramme</code> :</p>
<pre class="code vim vimft-xslt"><span class="Comment"><?</span><span class="Type">xml</span><span class="Type">version</span>=<span class="Constant">"1.0"</span><span class="Type">encoding</span>=<span class="Constant">"UTF-8"</span><span class="Comment">?></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">stylesheet</span><span class="Type">version</span>=<span class="Constant">"1.0"</span><span class="Type">xmlns</span><span class="Comment">:</span><span class="Type">xsl</span>=<span class="Constant">"<a href="http://www.w3.org/1999/XSL/Transform">http://www.w3.org/1999/XSL/Transform</a>"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">output</span><span class="Type">method</span>=<span class="Constant">"text"</span><span class="Type">encoding</span>=<span class="Constant">"UTF-8"</span><span class="Identifier"> /></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">strip-space</span><span class="Type">elements</span>=<span class="Constant">"*"</span><span class="Identifier">/></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">match</span>=<span class="Constant">"/xjx/cmd[@n='as']"</span><span class="Identifier">></span><span class="Type">&</span><span class="Statement">lt</span><span class="Type">;</span>!DOCTYPE html [
<span class="Type">&</span><span class="Statement">lt</span><span class="Type">;</span>!ENTITY nbsp "<span class="Type">&</span><span class="Statement">amp</span><span class="Type">;</span>#160;"<span class="Type">&</span><span class="Statement">gt</span><span class="Type">;</span>
]<span class="Type">&</span><span class="Statement">gt</span><span class="Type">;</span>
<span class="Type">&</span><span class="Statement">lt</span><span class="Type">;</span>html<span class="Type">&</span><span class="Statement">gt</span><span class="Type">;&</span><span class="Statement">lt</span><span class="Type">;</span>body<span class="Type">&</span><span class="Statement">gt</span><span class="Type">;</span><span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"text()"</span><span class="Identifier">/></span><span class="Type">&</span><span class="Statement">lt</span><span class="Type">;</span>/body<span class="Type">&</span><span class="Statement">gt</span><span class="Type">;&</span><span class="Statement">lt</span><span class="Type">;</span>/html<span class="Type">&</span><span class="Statement">gt</span><span class="Type">;</span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">match</span>=<span class="Constant">"/xjx/cmd[@n!='as']"</span><span class="Identifier">/></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">stylesheet</span><span class="Identifier">></span>
</pre>
<p>Notez la génération d'un DOCTYPE définissant l'entité "nbsp", définie en HTML mais pas en XML.</p>
<p>Pour l'appliquer :</p>
<pre class="terminal"><samp class="prompt">$ </samp><kbd>xsltproc <a href="http://o.mengue.free.fr/xmltv/leprogramme.xslt" type="text/xsl">leprogramme.xslt</a> a.xml > b.xml</kbd>
</pre>
<p>On peut maintenant ouvrir ce fichier dans un navigateur pour vérifier que le document XML est valide. Ce n'est pas le cas :</p>
<ul>
<li>Il y a des entités mal formées (voir par exemple l'émission D&CO le dimanche soir sur M6)</li>
<li>Les blocs JSON contiennent contiennent des tags <code><br></code> non fermés.</li>
</ul>
<p>De plus, il serait bien utile d'extraire les données JSON sous forme de XML pour pouvoir continuer notre prototypage avec XSLT. Appellons Perl et le module JSON à la rescousse !</p>
<pre class="terminal"><samp class="prompt">$ </samp><kbd>xsltproc leprogramme.xslt a.xml | perl -M<a href="http://search.cpan.org/%7Emakamaka/JSON-2.06/lib/JSON.pm">JSON</a> -npe '
<span class="code vim vimft-perl"><span class="Statement">if</span> (m@^ *<div id=<span class="Constant">"</span><span class="Constant">(data_[^</span><span class="Constant">"</span>]*)<span class="Constant">"</span><span class="Constant"> style=</span><span class="Constant">"</span>display:none;<span class="Constant">"</span><span class="Constant">>([^}]*})</div>@) {</span>
<span class="Constant"> (</span><span class="Identifier">$id</span><span class="Constant">,</span><span class="Identifier">$data</span><span class="Constant">)=(</span><span class="Identifier">$1</span><span class="Constant">,jsonToObj(</span><span class="Identifier">$2</span><span class="Constant">));</span>
<span class="Identifier">$data</span><span class="Constant"> = join(</span><span class="Constant">"</span>\n<span class="Constant">"</span><span class="Constant">, map { qq|<div class=</span><span class="Constant">"</span><span class="Identifier">$_</span><span class="Constant">"</span><span class="Constant">></span><span class="Identifier">$data->{$_}</span><span class="Constant"></div>| } keys %</span><span class="Identifier">$data</span><span class="Constant">);</span>
<span class="Identifier">$_</span><span class="Constant"> = </span><span class="Constant">"</span><div id=\<span class="Constant">"</span><span class="Identifier">$id</span><span class="Special">\"</span><span class="Constant">></span><span class="Identifier">$data</span><span class="Constant"></div></span><span class="Constant">"</span>;
s@<span class="Identifier"><br></span>@<br/><span class="Identifier">@g</span>
}
<span class="Statement">s/</span><span class="Constant">&</span><span class="Special">([^;]{8})</span><span class="Statement">/</span><span class="Constant">&amp;</span><span class="Identifier">$1</span><span class="Statement">/g</span>;</span>' > c.xml</kbd>
</pre>
<p>Lançons un petit test d'extraction XSLT : nous allons lister les chaînes avec <a href="http://o.mengue.free.fr/xmltv/chaines.xslt" type="text/xsl"><code>chaines.xslt</code></a>.</p>
<pre class="code vim vimft-xslt"><span class="Comment"><?</span><span class="Type">xml</span><span class="Type">version</span>=<span class="Constant">"1.0"</span><span class="Type">encoding</span>=<span class="Constant">"UTF-8"</span><span class="Comment">?></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">stylesheet</span><span class="Type">version</span>=<span class="Constant">"1.0"</span><span class="Type">xmlns</span><span class="Comment">:</span><span class="Type">xsl</span>=<span class="Constant">"<a href="http://www.w3.org/1999/XSL/Transform">http://www.w3.org/1999/XSL/Transform</a>"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">output</span><span class="Type">method</span>=<span class="Constant">"text"</span><span class="Type">encoding</span>=<span class="Constant">"UTF-8"</span><span class="Identifier"> /></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">match</span>=<span class="Constant">"/html/body/div"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"substring(@id, 6)"</span><span class="Identifier">/></span>: <span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='chaine']/@title"</span><span class="Identifier">/></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">text</span><span class="Identifier">></span><span class="Type">&</span><span class="Statement">#10</span><span class="Type">;</span><span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">text</span><span class="Identifier">></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Identifier">></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">stylesheet</span><span class="Identifier">></span>
</pre>
<p>Et voici le résultat :</p>
<pre class="terminal"><samp class="prompt">$ </samp><kbd>xsltproc <a href="http://o.mengue.free.fr/xmltv/chaines.xslt" type="text/xsl"><code>chaines.xslt</code></a> b.xml</kbd>
<samp>192: TF 1
4: France 2
80: France 3
34: Canal+
47: France 5
111: Arte
118: M6</samp></pre>
<p>OK. Nous avons le champ libre pour transformer notre document en XMLTV avec <a href="http://o.mengue.free.fr/xmltv/xmltv.xslt"><code>xmltv.xslt</code></a> :</p>
<pre class="code vim vimft-xslt"><span class="Comment"><?</span><span class="Type">xml</span><span class="Type">version</span>=<span class="Constant">"1.0"</span><span class="Type">encoding</span>=<span class="Constant">"UTF-8"</span><span class="Comment">?></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">stylesheet</span><span class="Type">version</span>=<span class="Constant">"1.0"</span><span class="Type">xmlns</span><span class="Comment">:</span><span class="Type">xsl</span>=<span class="Constant">"<a href="http://www.w3.org/1999/XSL/Transform">http://www.w3.org/1999/XSL/Transform</a>"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">output</span><span class="Type">method</span>=<span class="Constant">"xml"</span><span class="Type">indent</span>=<span class="Constant">"yes"</span><span class="Type">encoding</span>=<span class="Constant">"UTF-8"</span><span class="Identifier"> /></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">strip-space</span><span class="Type">elements</span>=<span class="Constant">"*"</span><span class="Identifier">/></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">match</span>=<span class="Constant">"/html"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Identifier">tv</span><span class="Type">generator-info-name</span>=<span class="Constant">"Télérama → XMLTV"</span>
<span class="Type">source-info-url</span>=<span class="Constant">"<a href="http://television.telerama.fr/tele/grille.php">http://television.telerama.fr/tele/grille.php</a>"</span>
<span class="Type">source-info-name</span>=<span class="Constant">"Télérama"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">apply-templates</span><span class="Type">mode</span>=<span class="Constant">"channel"</span><span class="Type">select</span>=<span class="Constant">"body/div/div[@class='chaine']"</span><span class="Identifier"> /></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">apply-templates</span><span class="Type">mode</span>=<span class="Constant">"programme"</span><span class="Type">select</span>=<span class="Constant">"body/div/div/div/div[contains(@class, 'emission')]/div/div[substring(@id, 0, 6)='data_']"</span><span class="Identifier"> /></span>
<span class="Identifier"></tv></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">match</span>=<span class="Constant">"text()"</span><span class="Type">priority</span>=<span class="Constant">"-1"</span><span class="Identifier">/></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">mode</span>=<span class="Constant">"channel"</span><span class="Type">match</span>=<span class="Constant">"div"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Identifier">channel</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Type">name</span>=<span class="Constant">"id"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"substring(../@id, 6)"</span><span class="Identifier">/></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Identifier">display-name</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"./@title"</span><span class="Identifier">/></span><span class="Identifier"></display-name></span>
<span class="Identifier"><</span><span class="Identifier">icon</span><span class="Type">width</span>=<span class="Constant">"40"</span><span class="Type">height</span>=<span class="Constant">"40"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Type">name</span>=<span class="Constant">"src"</span><span class="Identifier">></span><a href="http://icon-telerama.sdv.fr/tele/imedia/images_chaines_tra/Transparent/40x40/">http://icon-telerama.sdv.fr/tele/imedia/images_chaines_tra/Transparent/40x40/</a><span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"substring(../@id, 6)"</span><span class="Identifier">/></span>.gif<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Identifier">></span>
<span class="Identifier"></icon></span>
<span class="Identifier"></channel></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">mode</span>=<span class="Constant">"programme"</span><span class="Type">match</span>=<span class="Constant">"div"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Identifier">programme</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Type">name</span>=<span class="Constant">"channel"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='Id_Chaine']"</span><span class="Identifier">/></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Type">name</span>=<span class="Constant">"start"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">apply-templates</span><span class="Type">mode</span>=<span class="Constant">"xmltv-time-from-iso-8601"</span><span class="Type">select</span>=<span class="Constant">"div[@class='Date_Debut']"</span><span class="Identifier">/></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Type">name</span>=<span class="Constant">"stop"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">apply-templates</span><span class="Type">mode</span>=<span class="Constant">"xmltv-time-from-iso-8601"</span><span class="Type">select</span>=<span class="Constant">"div[@class='Date_Fin']"</span><span class="Identifier">/></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Type">name</span>=<span class="Constant">"showview"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='ShowViewFr']"</span><span class="Identifier">/></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">attribute</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Identifier">title</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='Titre']"</span><span class="Identifier">/></span>
<span class="Identifier"></title></span>
<span class="Identifier"><</span><span class="Identifier">sub-title</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='Sous_Titre']"</span><span class="Identifier">/></span>
<span class="Identifier"></sub-title></span>
<span class="Identifier"><</span><span class="Identifier">desc</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='resume_long']"</span><span class="Identifier">/></span>
<span class="Identifier"></desc></span>
<span class="Identifier"><</span><span class="Identifier">category</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='Type']"</span><span class="Identifier">/></span>
<span class="Identifier"></category></span>
<span class="Identifier"><</span><span class="Identifier">length</span><span class="Type">units</span>=<span class="Constant">"seconds"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"div[@class='DureeEnSecondes']"</span><span class="Identifier">/></span>
<span class="Identifier"></length></span>
<span class="Identifier"></programme></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Type">mode</span>=<span class="Constant">"xmltv-time-from-iso-8601"</span><span class="Type">match</span>=<span class="Constant">"*|@*"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">value-of</span><span class="Type">select</span>=<span class="Constant">"concat(substring(.,0,5),substring(.,6,2),substring(9,2),substring(.,12,2),substring(.,15,2))"</span><span class="Identifier">/></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">template</span><span class="Identifier">></span>
<span class="Identifier"></</span><span class="Special">xsl</span><span class="Comment">:</span><span class="Statement">stylesheet</span><span class="Identifier">></span>
</pre>
<p>Il n'y a plus qu'à enchaîner toutes les étapes dans un script shell (<a href="http://o.mengue.free.fr/xmltv/tv.sh"><code>tv.sh</code></a>) :</p>
<pre class="terminal unix"><samp class="prompt">$ </samp><kbd>./<a href="http://o.mengue.free.fr/xmltv/tv.sh">tv.sh</a> tv.2008-02-14T23:00:00.xml 2008-02-14T23:00:00</kbd>
</pre>
<p>Et voici quelques extraits du résultat :</p>
<pre class="code vim vimft-xml"><span class="Comment"><?</span><span class="Type">xml</span><span class="Type">version</span>=<span class="Constant">"1.0"</span><span class="Type">encoding</span>=<span class="Constant">"UTF-8"</span><span class="Comment">?></span>
<span class="Identifier"><</span><span class="Identifier">tv</span><span class="Type">generator-info-name</span>=<span class="Constant">"Télérama → XMLTV"</span><span class="Type">source-info-url</span>=<span class="Constant">"<a href="http://television.telerama.fr/tele/grille.php">http://television.telerama.fr/tele/grille.php</a>"</span><span class="Type">source-info-name</span>=<span class="Constant">"Télérama"</span><span class="Identifier">></span>
<span class="Comment"><!</span><span class="Comment">-- […] --</span><span class="Comment">></span>
<span class="Identifier"><</span><span class="Identifier">channel</span><span class="Type">id</span>=<span class="Constant">"111"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Identifier">display-name</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>Arte<span class="Identifier"></display-name></span>
<span class="Identifier"><</span><span class="Identifier">icon</span><span class="Type">width</span>=<span class="Constant">"40"</span><span class="Type">height</span>=<span class="Constant">"40"</span><span class="Type">src</span>=<span class="Constant">"<a href="http://icon-telerama.sdv.fr/tele/imedia/images_chaines_tra/Transparent/40x40/111.gif">http://icon-telerama.sdv.fr/tele/imedia/images_chaines_tra/Transparent/40x40/111.gif</a>"</span><span class="Identifier">/></span>
<span class="Identifier"></channel></span>
<span class="Comment"><!</span><span class="Comment">-- […] --</span><span class="Comment">></span>
<span class="Identifier"><</span><span class="Identifier">programme</span><span class="Type">channel</span>=<span class="Constant">"111"</span><span class="Type">start</span>=<span class="Constant">"2008020000"</span><span class="Type">stop</span>=<span class="Constant">"2008020220"</span><span class="Type">showview</span>=<span class="Constant">"2779370"</span><span class="Identifier">></span>
<span class="Identifier"><</span><span class="Identifier">title</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>Arizona Dream<span class="Identifier"></title></span>
<span class="Identifier"><</span><span class="Identifier">sub-title</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">/></span>
<span class="Identifier"><</span><span class="Identifier">desc</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>Axel, un orphelin de 20 ans, vit davantage dans un monde rempli de rêves et de poissons volants qu'à New York, où il habite. Par l'esprit, il se «transporte» souvent sur la banquise lointaine et ...<span class="Identifier"></desc></span>
<span class="Identifier"><</span><span class="Identifier">category</span><span class="Type">lang</span>=<span class="Constant">"fr"</span><span class="Identifier">></span>Film<span class="Identifier"></category></span>
<span class="Identifier"><</span><span class="Identifier">length</span><span class="Type">units</span>=<span class="Constant">"seconds"</span><span class="Identifier">></span>8400<span class="Identifier"></length></span>
<span class="Identifier"></programme></span>
<span class="Comment"><!</span><span class="Comment">-- […] --</span><span class="Comment">></span>
<span class="Identifier"></tv></span>
</pre>
<p>Pour visualiser le résultat obtenu sous une forme plus lisible pour un humain, je vous recommande d'essayer <a href="http://www.ericandchar.com/xsltvgrid/" hreflang="en">XSLTv</a>. Vous enregistrez le fichier XMLTV sous <code>tv.xml</code> dans le répertoire de XSLTv et vous chargez <code>tv.html</code> dans votre navigateur. Vous obtenez <a href="http://o.mengue.free.fr/xmltv/xsltv/tv.html" rel="noindex nofollow">ceci</a> :</p>
<p style="text-align: center;"><a href="http://o.mengue.free.fr/xmltv/xsltv/tv.html" rel="noindex nofollow"><img src="http://o.mengue.free.fr/blog.dc/images/20080214-grille-telerama-xsltv-488.jpg" alt="Le fichier XMLTV visualisé avec XSLTv" /></a></p>
<p>Ce n'est pas visuellement aussi beau que le site Télérama, mais pensez que ce n'est que l'une des multiples utilisations du format XMLTV : ces données peuvent aussi être utilisées par exemple pour piloter un magnétoscope numérique.</p>
<p>L'étape suivante sera de programmer un proxy en PHP, mais je vous en parlerais plus tard.</p>
<hr>
<p>Fichiers joints :</p>
<ul>
<li><a href="http://o.mengue.free.fr/xmltv/leprogramme.xslt" type="text/xsl"><code>leprogramme.xslt</code></a></li>
<li><a href="http://o.mengue.free.fr/xmltv/chaines.xslt" type="text/xsl"><code>chaines.xslt</code></a></li>
<li><a href="http://o.mengue.free.fr/xmltv/xmltv.xslt" type="text/xsl"><code>xmltv.xslt</code></a></li>
<li><a href="http://o.mengue.free.fr/xmltv/tv.sh" type="application/x-shellscript"><code>tv.sh</code></a></li>
</ul>
<p><strong>MàJ 2008-03-02 :</strong> J'ai mis à jour tv.sh et xmltv.xslt. Voir <a href="http://o.mengue.free.fr/blog/2008/03/02/53-telerama-xmltv-mise-a-jour">ce billet</a>.</p>http://o.mengue.free.fr/blog/post/2008/02/11/51-les-programmes-tele-de-telerama-en-xmltv#comment-formhttp://o.mengue.free.fr/blog/feed/atom/comments/51