Playlist Maker

by Jim Chapman
Home| Where we live | Jim| Isabel| James| Edward]

/*
 * 
 * PlaylistMaker/Class1.cs
 * Copyright (C) 2002 Jim Chapman
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 (and no other version)
 * of the GNU General Public License
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * If you require a copy of the GNU General Public License write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 * Jim Chapman may be reached at: home@jim-chapman.net
 *
 *
 * Revision History
 * ================
 * Who             When          What
 * ---             ----          ----
 * Jim Chapman     11 May 2002   Released.
 *
 * 
 * How to Use the Program
 * ======================
 *  This program requires the .NET Framework to be installed
 *  on the computer you're running it on.  If you haven't already
 *  installed the framework, go to
 *  http://msdn.microsoft.com/netframework/prodinfo/getdotnet.asp
 *  to do so.
 * 
 *  This program should be run from a console window.
 * 
 *    If there is a command line argument, it should be the name of
 *  a directory.  The program will use that directory as its base
 *  directory.  If there is no command line argument, the
 *  program's base directory will be the console window's current
 *  directory.
 * 
 *  The program (like it says on the label) makes playlists.
 *  That is to say it traverses the base directory and the entire
 *  directory tree below it, looking for MP3 files.  For every
 *  directory it finds which contains MP3 files, it build a M3U
 *  (playlist) file listing the names of all those MP3 files, in
 *  the order of their last-modified dates/times.
 *  Actually, it builds two files - one, it places in the base directory,
 *  and the other it places in the same directory as the MP3 files. 
 *  The names of the playlist files begin with "!_PLAY", and then
 *  consist of the names of all intermediate directories between
 *  the base directory and the MP3 file directory, concatenated
 *  together and separated by "_" characters.
 * 
 *  This behaviour is simpler than it sounds.  Run the program on
 *  a directory structure containg some MP3 files, and the results
 *  will make it clear what is going on.
 * 
 */
using System;
using System.IO;
using System.Collections;
using System.Text;

namespace PlaylistMaker
{
   /// <summary>
   /// Summary description for Class1.  Class1 is a playlist-maker, but I
   /// can't call it that, because the VisualStudio already decided to use that
   /// name for the namespace.  Thanks, Bill!
   /// </summary>
   class Class1
   {
      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main(string[] args)
      {
         Class1 c = new Class1();

         // If there is a first command line argument, treat it
         // as a directory name, and try to CD to it.
         if(args.Length>0)
         {
            try
            {
               Directory.SetCurrentDirectory(args[0]);
            }
            catch
            {
               Console.WriteLine("Cannot change to directory "+args[0]);
            }
         }

         // Call the method that does all the work.  Begin with
         // an empty 'directory path' list, because the path
         // that is of interest begins at the base directory.
         c.MakePlaylists(new ArrayList());

      }

      /// <summary>
      /// MakePlaylists
      /// -------------
      /// This method performs all the functions of the program.
      /// Its parameter is a list of directory names on the path
      /// leading from the base directory to the current one.
      /// </summary>
      public void MakePlaylists(ArrayList path)
      {
         // First, list all the directories in the current
         // directory, and work through them, CDing to each
         // in turn, and recursively calling this method
         // for each.
         string [] dirs = Directory.GetFileSystemEntries(Directory.GetCurrentDirectory());
         foreach (string s in dirs)
         {
            FileInfo d=new FileInfo(s);
            string dirname=d.Name;
            if((d.Attributes & FileAttributes.Directory)!=0)
            {
               ArrayList childDir=new ArrayList(path);
               childDir.Add(dirname);

               Directory.SetCurrentDirectory(s);
               MakePlaylists(childDir);
               Directory.SetCurrentDirectory("..");
            }
         }

         // Now list all the MP3 files in the current
         // directory.
         string [] files = Directory.GetFiles(Directory.GetCurrentDirectory(),"*.mp3");

         if(files.Length>0)
         {
            // Build up the name of the playlist file.  For
            // instance, if the base directory were
            // C:\FOO\BAR and the current directory were
            // C:\FOO\BAR\WITH\BILL\GATES then the playlist
            // name would end up being:
            // "!_PLAY_WITH_BILL_GATES.M3U"
            string plname="!_PLAY";
            foreach (string d in path)
            {
               plname+="_"+d;
            }
            plname+=".m3u";

            // Sort the file names into ascending order of
            // their last-updated times.  This is done
            // inefficiently (or at least, I'm relying on Bill
            // Gates to cache file information for me, which
            // is somewhat optimistic).
            Array.Sort(files,new CompareTimes());

            // Now create the playlist file in the current
            // directory.  The only slightly complex bit is that
            // I use codepage-1252 encoding for the text in the
            // file.  That's the 'Windows' codepage (i.e. the one
            // that the OS uses for filenames and suchlike, and
            // it seems to be the encoding that MediaPlayer
            // and similar programs expect to use for the
            // filenames in playlists.
            FileStream fs = new FileStream(plname, FileMode.Create);
            StreamWriter sr = new StreamWriter (fs, Encoding.GetEncoding(1252));
            foreach (string s in files)
            {
               FileInfo f=new FileInfo(s);
               sr.WriteLine(f.Name);
            }
            sr.Close();

            // Next create the playlist file in the base
            // directory.  Two things change - firstly, the
            // name of the playlist file (because, to open
            // it from the current directory, we need to
            // prefix it with some ..\ strings, to represent
            // the fact it's up some number of directory levels,
            // and secondly all the MP3 filenames in the
            // playlist file need a bunch of directory names
            // (separated by "/") prefixed onto them, in order
            // to identify the subdirectory in which the MP3
            // file resides, relative to the base directory.
            // The 'firstly' part of this convoluted explanation
            // is handled by rootPath; the 'secondly' part by
            // 'mp3path'.
            string rootPath=".";
            string mp3path="";
            foreach (string d in path)
            {
               rootPath+="\\..";
               mp3path+=d+"\\";
            }
            plname=rootPath+"\\"+plname; // the name of the M3U file
            fs = new FileStream(plname, FileMode.Create);
            sr = new StreamWriter (fs, Encoding.GetEncoding(1252));
            foreach (string s in files)
            {
               FileInfo f=new FileInfo(s);
               sr.WriteLine(mp3path+f.Name);
            }
            sr.Close();
         }
      }
   }

   /// <summary>
   /// CompareTimes
   /// -------------
   /// This class is for use in the sorting logic above.  It
   /// lets us compare two strings, which are treated as file
   /// names, and which sort according to the last-updated
   /// times of the files named by those strings.  This is
   /// probably grossly inefficient.
   /// </summary>
   class CompareTimes : IComparer
   {
      public int Compare(Object o1, Object o2)
      {
         FileInfo f1=new FileInfo((string)o1);
         FileInfo f2=new FileInfo((string)o2);

         return f1.LastWriteTime.CompareTo(f2.LastWriteTime);
      }
   }
}



home@jim-chapman.net