Squelette de podcast adaptable à tous les besoins

Ceci est une « contribution pédagogique », qui montre par l’exemple comment développer une nouvelle fonctionnalité pour SPIP.

Cet article à été réalisé dans le cadre d’une formation au langage SPIP : son but est plus de montrer comment gérer le contexte des boucles SPIP que de fournir un outil de podcast ergonomique...

Pour installation d’un des squelettes fournis ci-dessous : téléchargez le dans le dossier /squelettes de votre site et renommez le podcast.html : c’est prêt !

But

A partir d’un squelette de podcast qui traite l’ensemble des articles du SPIP par une BOUCLE simple, on peut réaliser des modifications du contexte de cette BOUCLE pour restreindre les articles exposés dans le flux de différentes manières (par rubriques, selon le format des documents attachés, par mot clé attaché...).

Qu’est ce qu’un podcast ?

Un podcast est un simple fichier XML publié par un site web et qui se conforme à une structure lui permettant d’être lu et « compris » (i.e. d’être compatible) avec les logiciels de podcast tels que Juice et SongBird (open source) ou iTunes et Winamp (propriétaires)

Pour plus de précisions sur le XML en général voir : Le langage XML : structuration, validation et transformations.
Pour plus de précisions sur la structure d’un fichier de podcast voir Les flux RSS, podcast et vidéocast

Remarque :
le squelette par défaut de SPIP (« dist ») fourni un flux RSS (http://site.../spip.php?page=backend) qui fait un podcast minimaliste, mais fonctionnel voir par exemple pour spip-contrib

Structure du fichier de podcast attendu

un fichier de podcast « type »
le code commenté d’un fichier XML de podcast

L’examen d’un fichier XML de podcast (code du fichier type) montre que le squelette du podcast doit contenir 2 parties :

  • la partie qui débute le flux et qui permet de décrire le site émetteur et le podcast dans son ensemble. Cette partie est « fixe » : son contenu est indépendant des items diffusés, les informations quelle contient sont spécifique du site émetteur.
  • la liste des items : dans le SPIP chaque item correspondra à un article, le fichier média « podcasté » (i.e. associé à l’item) sera un document attaché. Cette liste est donc constituée par les n derniers articles publiés. Chaque item est structuré de façon identique.

Un squelette de podcast basique

Le squelette « de base »
Les 12 derniers articles sans aucun critère de filtrage

Pour générer le podcast, dans le dossier /squelettes, on va créer un fichier de squelette spécifique : podcast.html. Selon la règle générale de fonctionnement de SPIP, l’URL de ce fichier de squelette sera donc de la forme : http://site.../spip.php?page=podcast.

Le contenu de ce fichier sera donc un mélange de code SPIP et des balises XML typiques du format podcast :
-  la première partie du fichier (informations concernant le site et le podcast) va donc faire appel aux balises SPIP se rapportant au site (cf Les balises propres au site). Pour chaque élément descriptif on va insérer dans le XML la balise SPIP correspondant au contenu recherché : par exemple pour le titre et l’url du podcast :
<title>#NOM_SITE_SPIP</title> donnera <title>Site Truc</title>
et <link>#URL_SITE_SPIP</link> retourne <link>http://mon-site-spip.tld</link>
Remarque : les problèmes de validation du XML imposent un traitement des contenus pour éviter les caractères interdits : pour cela on applique l’enchaînement de filtres |textebrut|entites_html|entites_unicode sur les textes ou |texte_backend.

On obtient donc le code suivant pour la première partie :

#CACHE{3600}[(#HTTP_HEADER{Content-type: text/xml[; charset=(#CHARSET)]})]<?xml version="1.0"[ encoding="(#CHARSET)"]?>
<rss xmlns:itunes="http://www.itunes.com/DTDs/Podcast-1.0.dtd" version="2.0">
  <channel>
    <title>[(#NOM_SITE_SPIP|textebrut|entites_html|entites_unicode)]</title>
    <link>[(#URL_SITE_SPIP)]</link>
    [<description>
         (#DESCRIPTIF_SITE_SPIP|textebrut|entites_html|entites_unicode)
    </description>]
    <generator>SPIP</generator>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <language>#LANG</language>
    <copyright>
        [(#NOM_SITE_SPIP|textebrut|entites_html|entites_unicode)] 
        [(#DATE_annee|textebrut|entites_html|entites_unicode)]
    </copyright>
    <managingEditor>#EMAIL_WEBMASTER</managingEditor>
    <BOUCLE_webmaster(AUTEURS) {id_auteur=1}>
        <webMaster>[(#NOM|texte_backend)]</webMaster>
    </BOUCLE_webmaster>
    <pubDate>[(#DATE|affdate{'r'})]</pubDate>
    <lastBuildDate>
        [(#DATE_NOUVEAUTES|affdate)] [(#DATE_NOUVEAUTES|heures)]:
        [(#DATE_NOUVEAUTES|minutes)]:[(#DATE_NOUVEAUTES|secondes)]
    </lastBuildDate>
    <image>
      <url>[href="(#LOGO_SITE_SPIP||extraire_attribut{src}|url_absolue|texte_backend)"]</url>
      <title>#NOM_SITE_SPIP</title>
      <link>#URL_SITE_SPIP</link>
      <title>#NOM_SITE_SPIP</title>
    </image>
    <itunes:author>Nom du site</itunes:author>
    <itunes:link rel="image" type="video/png" href="http://www.url-du-site.org/adresse_images_site.png">Nom du site</itunes:link>
    [<itunes:image href="(#LOGO_SITE_SPIP||extraire_attribut{src}|url_absolue|texte_backend)" />]
    [<itunes:category text="(#DESCRIPTIF_SITE_SPIP|textebrut|entites_html|entites_unicode)" />]
    <itunes:category text="#LANG" />
    <itunes:explicit>no</itunes:explicit>

-  pour la partie dynamique du podcast (la liste des items), dans une première approche, on utilise une boucle ARTICLES qui permet de lister les derniers articles publiés (BOUCLE_article dans la suite de cet article) qui contient une boucle DOCUMENTS (BOUCLE_podcast) pour afficher les documents attachés (fichiers qui vont être placés dans une balise XML <enclosure>) :

<BOUCLE_article(ARTICLES) {par date} {inverse} {0,12}>
    <item>
      <title>[(#TITRE|texte_backend)]</title>
      <link>[(#URL_ARTICLE|url_absolue|unique)]</link>
      <description>[(#TEXTE|liens_absolus|texte_backend)]</description>
      <BOUCLE_categorie(RUBRIQUES){id_rubrique}{0,1}>
          <category>#TITRE</category>
      </BOUCLE_categorie>
      <pubDate>[(#DATE|affdate{'r'})]</pubDate>
      <BOUCLE_podcast(DOCUMENTS){id_article}{mode=document}>
          [<enclosure url="(#URL_DOCUMENT|url_absolue|unique)" length="[(#TAILLE)]" type="#MIME_TYPE" />]
       </BOUCLE_podcast>
      <guid isPermaLink="false">[(#URL_ARTICLE|url_absolue|unique)]</guid>
      <itunes:author>
           <BOUCLE_auteursb(AUTEURS){id_article}{", "}>
               [(#NOM|texte_backend)]
           </BOUCLE_auteursb>
      </itunes:author>
      [<itunes:subtitle>(#SOUS_TITRE|texte_backend)</itunes:subtitle>]
      <itunes:summary>[(#TEXTE|liens_absolus|texte_backend)]</itunes:summary>
      <itunes:keywords>
          <BOUCLE_articles_mots(ARTICLES) {id_mot} {par hasard} {0,4}>
              #TITRE
          </BOUCLE_articles_mots>
      </itunes:keywords>  
    </item>
</BOUCLE_article>


-  ...et pour que le fichier obtenu soit complet, il ne faut pas oublier de fermer les balises rss et channel ouvertes au départ du fichier :

    </channel>
</rss>

Ce squelette va donc retourner les 12 derniers articles publiés, avec pour chacun d’entre eux les documents attachés placés dans des balises enclosure.

Filtrer les documents mis dans <enclosure>

La première chose à faire est de ne sélectionner que les fichiers mp3 dans la balise enclosure : pour cela il faut modifier la BOUCLE_podcast de la façon suivante :

<BOUCLE_podcast(DOCUMENTS){id_article}{mode=document}{extension=mp3}>

Exclure les articles n’ayant pas de fichier mp3 attaché

Squelette « mp3 uniquement »
Sélectionne parmi les 12 derniers articles ceux ayant un mp3 en document attaché

L’amélioration la plus notable (à combiner avec les critères ci-dessous), consiste à ne sélectionner que les articles ayant au moins un fichier mp3 en document attaché. Pour cela on utilise la BOUCLE_podcast(DOCUMENTS) en étendant son champ d’action à l’ensemble de l’item.
Cette modification nécessite une réorganisation complète pour que la BOUCLE_podcast englobe l’ensemble de la balise <item>...</item> : le principe est d’utiliser le fait que si il n’existe pas de fichier mp3 renvoyé par cette boucle, on n’affiche pas la balise <item>...</item> i.e. l’article ne sera donc pas intégré dans le flux du podcast.

  <BOUCLE_article(ARTICLES) {id_rubrique ?} {par date} {inverse} {0,12}>
    <BOUCLE_podcast(DOCUMENTS){id_article}{mode=document}{extension=mp3}>
    <item>
     ...
    </item>
    </BOUCLE_podcast>
  </BOUCLE_article>

Le problème engendré par cette organisation de boucles imbriquées est que les éléments à afficher dans l’item peuvent êtres soit dépendant de la boucle DOCUMENTS (le <enclosure>...</enclosure> principalement) qui est la boucle en cours, soit de la boucle ARTICLES englobante. Pour appeler les balises SPIP de la boucle englobante BOUCLE_article on va donc utiliser la syntaxe suivante : #_article:NOM_BALISE pour remplacer les balises #NOM_BALISE que l’on utiliserait sans cette imbrication supplémentaire. La partie dynamique du squelette devient donc :

  <BOUCLE_article(ARTICLES) {id_rubrique ?} {par date} {inverse} {0,12}>
    <BOUCLE_podcast(DOCUMENTS){id_article}{mode=document}{extension=mp3}>
    <item>
      <title>[(#_article:TITRE|texte_backend)]</title>
      <link>[(#_article:URL_ARTICLE|url_absolue|unique)]</link>
      <description>[(#_article:TEXTE|liens_absolus|texte_backend)]</description>
      <BOUCLE_categorie(RUBRIQUES){id_rubrique}{0,1}>
          <category>#TITRE</category>
      </BOUCLE_categorie>
      <pubDate>[(#_article:DATE|affdate{'r'})]</pubDate>
      [<enclosure url="(#URL_DOCUMENT|url_absolue|unique)" length="[(#TAILLE)]" type="#MIME_TYPE" />]
      <guid isPermaLink="false">[(#_article:URL_ARTICLE|url_absolue|unique)]</guid>
      <itunes:author>
          <BOUCLE_auteursb(AUTEURS){id_article}{", "}>[(#NOM|texte_backend)]</BOUCLE_auteursb>
      </itunes:author>
      [<itunes:subtitle>(#_article:SOUS_TITRE|texte_backend)</itunes:subtitle>]
      <itunes:summary>[(#_article:TEXTE|liens_absolus|texte_backend)]</itunes:summary>
      <itunes:keywords><BOUCLE_mots(MOTS) {id_article=#_article:ID_ARTICLE} {par hasard} {0,4}{", "}>#TITRE,</BOUCLE_mots></itunes:keywords>  
    </item>
    </BOUCLE_podcast>
  </BOUCLE_article>

Améliorer la sélection des articles intégrés dans le flux

A partir des critères disponibles dans la boucle ARTICLES, plusieurs améliorations sont possibles / souhaitables à partir de la BOUCLE_article de base :
-  Ne sélectionner que les articles ayant un mot clé : pour cela on va créer un mot clé spécifique (« podcast » par exemple) qui sera attribués aux articles que l’on veut voit apparaitre dans le flux. La boucle sera alors modifiée de la façon suivante :

<BOUCLE_article(ARTICLES) {titre_mot = podcast} {par date} {inverse} {0,12}>


-  Dans le même ordre d’idée, on peut vouloir utiliser plusieurs mots clés (par exemple ici « podcast » et « sons ») : le code de la boucle sera alors :

<BOUCLE_article(ARTICLES) {titre_mot IN (podcast,sons)}{par date} {inverse} {0,12}>


-  Toujours dans la même lignée on peut utiliser tous les mots clés d’un groupe de mots clés (par exemple ici le groupe multimédia) :

<BOUCLE_article(ARTICLES) {type_mot=multimédia}{par date} {inverse} {0,12}>


-  Il est également possible de faire une sélection par mots clés sur les rubriques (par ex, ne podcaster que sur les rubriques « Anglais », « Italien » et « Allemand »). Pour ce faire on attribue le mot clé « podcast » aux rubriques en question, puis on modifie le squelette de façon à trier sur les rubriques avant de boucler dans les articles. Notez le {id_rubrique} de la BOUCLE_article : il est nécessaire pour préciser que cette boucle ne prend que les articles de la rubrique sélectionnée par la BOUCLE_tri_rub englobante.

<BOUCLE_tri_rub(RUBRIQUES){titre_mot=podcast}>
  <BOUCLE_article(ARTICLES) {id_rubrique} {par date} {inverse} {0,12}>
    <BOUCLE_podcast(DOCUMENTS){id_article}{mode=document}{extension=mp3}>
    <item>
      <title>[(#_article:TITRE|texte_backend)]</title>
      ....
    </item>
   </BOUCLE_podcast>
  </BOUCLE_article>
</BOUCLE_tri_rub>


-  Il est possible de combiner les différents niveaux de sélection : par exemple si on veut ne podcaster que les rubriques ayant le mot clé « podcast » en ne sélectionnant que les articles ayant le mot clé « sons », on fera alors :

<BOUCLE_tri_rub(RUBRIQUES){titre_mot=podcast}>
  <BOUCLE_article(ARTICLES) {titre_mot=sons} {id_rubrique} {par date} {inverse} {0,12}>
Squelette « par mots rubriques et articles »
Sélectionne les articles ayant le mot clé « podcast » uniquement dans les rubriques ayant le mot clé « podcast »
Squelette « par mots rubriques »
Sélectionne les articles dans les rubriques ayant le mot clé « podcast »
Squelette « par mots articles »
Sélectionne les articles ayant le mot clé « podcast » dans toutes les rubriques


Générer un flux pour chaque rubrique

Jusque là le squelette de podcast émet un flux général (à l’échelle de l’ensemble du site) unique. On peut vouloir émettre un flux de podcast par rubrique (il semble légitime de vouloir un flux différent pour l’anglais, l’italien et l’allemand par exemple) : il va donc falloir récupérer le contexte dans lequel se trouve appelé le squelette. Ce contexte de rubrique sera donné par l’url d’appel de la page : de la façon suivante : .../spip.php?id_rubrique=12 ou .../?id_rubrique=12. Dans tous les cas cela permet de pouvoir récupérer un id_rubrique dans le squelette... et de l’utiliser pour limiter la BOUCLE_article de la façon suivante :

  <BOUCLE_article(ARTICLES) {id_rubrique} {par date} {inverse} {0,12}>
    <BOUCLE_podcast(DOCUMENTS){id_article}{mode=document}{extension=mp3}>
      <item>
        <title>[(#_article:TITRE|texte_backend)]</title>
       ...
      </item>
   </BOUCLE_podcast>
  </BOUCLE_article>

Ce squelette n’affichera que les articles de la rubrique n°12 si on l’appelle avec : .../spip.php ?page=podcast&id_rubrique=12 alors qu’il n’affichera que les articles de la rubrique n°14 si il est appelé par .../spip.php ?id_rubrique=14&page=podcast [1]

Le squelette « par rubrique »
Sélectionne les articles de la rubrique passée dans l’url ou tout le site si pas de id_rubrique=...

Le problème de ce squelette est qu’il ne retourne rien si il n’y a pas d’id_rubrique défini dans l’url... On peut améliorer la chose en faisant :

  <BOUCLE_article(ARTICLES) {id_rubrique ?} {par date} {inverse} {0,12}>

Le  ? du critère {id_rubrique ?} permettant de faire une sélection sur toutes les rubriques si il n’existe pas de contexte de rubrique passé par l’url.
On se retrouve donc avec le beurre et l’argent du beurre : un flux de podcast général pour tout le site en appelant .../spip.php ?page=podcast et un flux spécifique par rubrique avec .../spip.php ?page=podcast&id_rubrique=XX !

Notes

[1le premier séparateur de l’url est  ?, les suivants sont &

Discussion

2 discussions

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom