Construction fichiers m3u avec Apache Commons

De EjnTricks

Cet article va présenter comment effectuer reconstruire les fichiers m3u dans une arborescence de fichier mp3. Ce programme a été motivé suite à des fichiers corrompus dans une arborescence existante.

Tout le code présenté est disponible ici : http://www.jouvinio.net/svn/study/trunk/apacheCommons


Hand-icon.png Votre avis

Nobody voted on this yet

 You need to enable JavaScript to vote


Study icon.png Objectif

L'arborescence est de parcourir une arborescence et à chaque fois qu'un fichier m3u est trouvé, celui-ci est reconstruit en injectant la liste des fichiers mp3 se trouvant dans le même répertoire, triés par ordre alphabétique en ignorant la casse.


Java format icon.png Utilitaires

Une seule instance de Transformer est mise en place afin d'extraire le nom des fichiers depuis les instances de File. A première vue, ceci n'est pas nécessaire, mais elle sera très utile lors de l'exécution, au moment de parcourir les fichiers. Le code est très simple et effectue uniquement l'extraction du nom du fichier lors de la transformation.

package fr.ejn.tutorial.apache.commons.tool.mp3.transformer;

import org.apache.commons.collections4.Transformer;

import java.io.File;

/**
 * Transformer instance on File instance and return the file name.
 *
 * <p>
 * Not null safe.
 * </p>
 *
 * @author Etienne Jouvin
 *
 */
public final class FileNameTransformer implements Transformer<File, String> {

  /**
   * Default instance.
   */
  public static final FileNameTransformer INSTANCE = new FileNameTransformer();

  /**
   * Private constructor.
   */
  private FileNameTransformer() {
  }

  /** {@inheritDoc} */
  @Override
  public String transform(File input) {
    return input.getName();
  }

}


Start-icon.png Exécution

La classe d'exécution va permettre d'effectuer simplement le traitement. Le parcours de l'arborescence est facilité en étendant la classe DirectoryWalker. Celle-ci permet de spécifier des filtres sur les répertoires parcourus, ainsi que les fichiers listés. Pour cet exemple, tous les répertoires sont parcourus et seul les fichiers avec l'extension m3u sont pris en compte.

Lors du traitement de chaque fichier, par conséquent les fichiers m3u, tous les fichiers avec l'extension mp3 du même répertoire sont récupérés et triés par ordre alphabétique dans une liste. Cette liste est ensuite utilisée pour écrire dans le fichier m3u l'ensemble des noms de fichiers.

Malgré toutes ces manipulations, le code mis en place est très simple.

package fr.ejn.tutorial.apache.commons.tool.mp3;

import fr.ejn.tutorial.apache.commons.tool.mp3.transformer.FileNameTransformer;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.io.DirectoryWalker;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.comparator.NameFileComparator;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Tutorial class used to correct existing m3u file in folders, using tools from Apache Commons.
 * <ul>
 * <li>DirectoryWalker to walk into a folder hierarchy;</li>
 * <li>IterableUtils.toList to convert iterator into a list;</li>
 * <li>CollectionUtils.collect to transform a list</li>
 * </ul>
 *
 * <p>
 * Walk into a folder hierarchy. When a folder contains a m3u file, read all mp3 files in that
 * folder Sort according the file name. And then write the listing into the m3u file.
 * </p>
 *
 * @author Etienne Jouvin
 *
 */
public class FixM3u extends DirectoryWalker<String> {

  private static final Logger LOGGER = LoggerFactory.getLogger(FixM3u.class);

  /**
   * Default constructor, specify :
   * <ul>
   * <li>directoryFilter with TrueFileFilter.TRUE, all folders are valid;
   * <li>fileFilter with instance of WildcardFileFilter, to stop on m3u file;
   * <li>depthLimit to -1.
   * </ul>
   */
  public FixM3u() {
    super(TrueFileFilter.TRUE, new WildcardFileFilter("*.m3u", IOCase.INSENSITIVE), -1);
  }

  /**
   * Start method to run the tool.
   *
   * @param path Path to explore.
   * @throws IOException IO Exception.
   */
  public void fixFile(String path) throws IOException {
    List<String> res = new ArrayList<>();
    File folder = new File(path);

    walk(folder, res);
  }

  /** {@inheritDoc} */
  @Override
  protected void handleFile(File file, int depth, Collection<String> results) throws IOException {
    File parentFolder = file.getParentFile();

    List<File> mp3s = IterableUtils
        .toList(FileUtils.listFiles(parentFolder, new String[] { "mp3" }, false));
    Collections.sort(mp3s, NameFileComparator.NAME_INSENSITIVE_COMPARATOR);

    Collection<String> toWrite = CollectionUtils.collect(mp3s, FileNameTransformer.INSTANCE);

    try {
      FileUtils.writeLines(file, "ISO-8859-1", toWrite);
    } catch (IOException ioException) {
      LOGGER.error("Failed to write file " + file, ioException);
      throw new CancelException(file, depth);
    }
  }

}


Dans le constructeur par défaut, l'instance TrueFileFilter.TRUE est utilisé pour filtrer les répertoires. Celui-ci est totalement permissif. Le second argument permet de spécifier le filtre sur les fihciers pris en compte. L'instance WildcardFileFilter est utilisé afin de spécifier le pattern *.m3u permettant de filtrer sur l'extension m3u. Le troisième argument -1 permet de lever la restriction sur le niveau de profondeur de l'arborescence.

La fonction fixFile est le seul code réellement spécifique. Il permet de déclencher le parcours de l'arborescence à partir d'une emplacement souhaité. Une instance de List est requise pour l'exécution, même si celle-ci ne sera pas utilisée.

Puis, la méthode handleFile est exécutée pour chacun des fichiers (et non pas des répertoires) parcourus. En entrant dans cette méthode, les fichiers seront ceux avec l'extension m3u. Le répertoire parent est alors récupéré et la liste des fichiers mp3 est construite. Il est nécesaire de construire une liste, car c'est l'interface qui est acceptée ensuite par la fonction sort de la classe Collection.

    List<File> mp3s = IterableUtils
        .toList(FileUtils.listFiles(parentFolder, new String[] { "mp3" }, false));
    Collections.sort(mp3s, NameFileComparator.NAME_INSENSITIVE_COMPARATOR);

A noter, l'utilisation de IterableUtils.toList qui va permettre de transformer la collection retournée en liste. Le tri est effectué à l'aide du comparateur disponible NameFileComparator.NAME_INSENSITIVE_COMPARATOR. Il n'y a donc rien à faire.

Après cette exécution, la liste contient l'ensemble des fichiers mp3 du répertoire, triés selon le nom. C'est sur cette liste que la transformation sera exécutée pour extraire à partir des instances File le nom du fichier.

Enfin, l'écriture du fichier est réalisée simplement par la fonction writeLines de la classe FileUtils qui permet d'écrire dans un fichier tous les élements d'une collection en tant que ligne.

Le test unitaire FixM3uTest permet de valider le fonctionnement.