Publié par

Il y a 9 ans -

Temps de lecture 6 minutes

NoThunes, MediaPlayer et moteur de recherche

Projet NoThunes
Pour le quatrième volet de notre projet de plateforme de musique libre, NoThunes, nous allons ajouter les dernières fonctionnalités du backlog original :

  • En tant qu’internaute, je dois pouvoir écouter un morceau
  • En tant qu’internaute, je dois pouvoir rechercher des morceaux par mots-clés

Pour cela, nous passerons par l’utilisation de 2 plugins très pratiques :

  • Sound Manager, qui offre des tags GSP pour ajouter facilement des fonctions de media player dans les vues
  • Searchable, qui apporte des fonctionnalités de recherche basées sur Compass (et donc indirectement sur le moteur Lucene)

Suivez le guide …

En tant qu’internaute, je dois pouvoir écouter un morceau

Pour rappel, dans les épisodes précédents, nous avons offerts :

  • aux utilisateurs membres : la possibilité d’uploader des morceaux de musique.
  • aux internautes : la possibilité de naviguer dans les morceaux disponibles en filtrant par Band et Album, et de les télécharger.

Aujourd’hui nous allons leur permettre d’écouter les morceaux avant de les télécharger. Précisons tout d’abord cette User Story un peu vague. Il s’agit ici de fournir une fonctionnalité très minimaliste, permettant de jouer un morceaux directement dans l’interface de navigation, pour « goûter » avant de télécharger.

Le périmètre de notre besoin est très restreint et il se trouve qu’un plugin Grails le comble parfaitement : sound-manager. Ce plugin permet de transformer n’importe quel lien pointant vers un fichier mp3 dans la page en un mini lecteur audio flash.

Commençons donc par installer le plugin :

grails install-plugin sound-manager

Si vous avez une bonne mémoire, vous vous rappelez de notre GSP grails-app/views/navigation/_trackList.gsp qui sert à afficher la liste des Tracks recherchés. Il nous suffit d’ajouter en tête de ce fichier :


  

Ce tag va, à chaque chargement de la page, rechercher les liens pointant vers des fichiers mp3 et les « entourer » avec un peu de sound-manager pour obtenir un mini flash-player dédié au lien.

Comme nous avons mis une image dans le lien vers le mp3, il reste possible de télécharger le morceaux en cliquant sur l’icône plutôt que sur le bouton Play.

And voilà ! 100% de notre petit besoin est rempli, et l’opération a dû prendre 20 minutes en comptant :

C’est presque allé trop vite pour en goûter toute la saveur.

Pour ceux qui veulent aller plus loin, sachez que ce plugin est basé sur la librairie du même nom, et qu’il est donc également possible d’utiliser l’intégralité de l’API, qui est très complète, pour obtenir un lecteur plus riche, éventuellement en HTML5.

En tant qu’internaute, je dois pouvoir rechercher des morceaux par mots-clés

Passons maintenant au moteur de recherche de notre navigation. Le but est ici d’ajouter un formulaire de recherche sur la page de navigation et de lancer la recherche sur les noms des Bands, Albums et Tracks existant en base. Là il y a un peu plus à faire. Tout d’abord, installons le plugin Searchable :

grails install-plugin searchable

Ce plugin est basé sur Compass, qui est lui même basé sur Apache Lucene, le moteur de recherche full-text Java Open Source le plus utilisé (notamment par Twitter depuis peu). Le première étape est de déterminer sur quelles données nous souhaitons baser nos recherches. Dans notre cas, nous avons choisi les champs name des classes de domaines Band, Album et Track.

Pour renseigner l’index du moteur Lucene, il suffit de rajouter cette ligne dans nos classes :

static searchable = [only: ['name']]

Si vous souhaitez faire des mappings plus complexes, il existe de nombreuses possibilités : regardez du coté de la section Mappings de la documentation.

Notre index est maintenant renseigné. Il nous faut une nouvelle méthode de contrôleur pour l’interroger. Pour plus de simplicité, nous la plaçons dans le NavigationController. Fait notable : la recherche par l’index Lucene remonte des entités incomplètes. Seuls les champs indexés sont renseignés. Par conséquent, pour l’affichage final, il faut interroger la base pour avoir tous les champs. Il faut également penser à modifier la chaîne de recherche pour la rendre compréhensible par Lucene. Nous utilisons ici une technique très basique, mais les query Lucene peuvent être autrement plus puissantes. Pour plus de détails, voir la documentation du moteur.

grails-app/controllers/fr/xebia/nothunes/domain/NavigationController.groovy

  /**
    * Methode d'instrumentation pour transformer la chaine de recherche du formulaire en query Lucene-compliant
    * @param query Chaine de recherche
    */
   def instrument(String query) {
      return query+"*"
   }
   
   /**
    * Methode de recherche full-text
    */
   def search = {
      String searchQuery = instrument(params.searchQuery)
      
      log.debug "search query is ${searchQuery}"
      
      def result = searchableService.search(searchQuery)
      
      // Les *.searchEvery remontent des entités incomplètes, il faudra les récupérer en base pour avoir tous les champs
      def albumResult = Album.searchEvery(searchQuery)
      def bandResult = Band.searchEvery(searchQuery)
      def trackResult = Track.searchEvery(searchQuery)
      
      def trackList = [] as Set
      def bandList = [] as Set
      def albumList = [] as Set
      
      // filtrage des tracks pour n'afficher que ceux qui ont un fichier audio
      trackResult.each {
         def fullTrack = Track.get(it.id)
         if (fullTrack.audioPath) {
            trackList.add(fullTrack)
            albumList.add(fullTrack.album)
            bandList.add(fullTrack.album.band)
         }
      }
      
      // filtrage des albums pour afficher uniquement ceux qui ont au moins un track avec un fichier audio
      albumResult.each {
         def hasOneCompleteTrack = false
         def fullAlbum = Album.get(it.id)
         fullAlbum.tracks.each { aTrack ->
            if (aTrack.audioPath) {
               trackList.add(aTrack)
               hasOneCompleteTrack = true
            }
         }
         
         if (hasOneCompleteTrack)  {
            albumList.add(fullAlbum)
            bandList.add(fullAlbum.band)
         }
      }
      
      bandResult.each {
         def fullBand = Band.get(it.id)
         fullBand.albums.each {anAlbum ->
            def hasOneCompleteTrack = false
            
            anAlbum.tracks.each { aTrack ->
               if (aTrack.audioPath) {
                  trackList.add(aTrack)
                  hasOneCompleteTrack = true
               }
            }
            
            if (hasOneCompleteTrack)  {
               bandList.add(fullBand)
               albumList.add(anAlbum)
            }
         }
      }
      
      // Rendu de la vue
      render (view:'list', model: [bandList: bandList, albumList: albumList, trackList: trackList, searchQuery: params.searchQuery])
   }

Dernière chose à ajouter : le formulaire de recherche dans la vue :

grails-app/views/navigation/list.gsp


   
   

La chaine est maintenant complète et nous pouvons lancer des requêtes pour interroger notre base.

Conclusion

L’ajout de ces deux fonctionnalités a été assez rapide. Si l’on possède les bons points d’entrée et que l’on prend la peine de fouiller le repository de plugins de Grails, il y a de fortes chances de trouver son bonheur rapidement. Il va de soit que les exemples donnés ici sont des usages basique. Les librairies tirées par les plugins sont plus complètes et permettent d’aller beaucoup plus loin.

Je me permet ici personnel, mais l’utilisation de Grails me remplit de joie :-). C’est rapide, c’est concis, et si j’en veux plus, je peux. Le chemin vers le code qui fonctionne est bien tracé par les documentations.

Dans le prochain épisode, nous verrons comment tester notre code et contrôler sa qualité.

Ressources

Publié par

Publié par Aurélien Maury

Aurélien est passionné par les frameworks web haute productivité comme Grails, JRuby on Rails ou Play! framework. Il est également intéressé par tous les aspects de performance et d'optimisation (mais aussi par la phytothérapie, la PNL, la basse électrique, la philosophie et pleins d'autres sujets). Il est également formateur au sein de Xebia Training .

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.