Paul Mendoza C# blog
Saturday, October 28, 2006
  m3u downloader to capture mp3 files

This example will go over how to build an application in C# 2.0 to batch download all of the files from an m3u file and save them to a folder on your computer. In this case, I’m saving them onto my T-Mobile Sidekick but the principle is the same. This application uses threads, file access and web file downloading and some basic Windows Forms tasks.

Today I was looking at Magnatune which is a site that has a lot of really good music by artists that’s all pretty much free. I really like the songs by Ehren Starks which was the reason that I was on the site and I’ve listened to the music before but what is nice about the music is that not only is it free but it’s all unprotected by the DRM that seems to be so pervasive across the internet. I wanted to download some of the files and transfer them to my computer and then put them on my T-Mobile Sidekick and I realized that if you click on the Play HiFi links on the Magnatune site for any CD or artist, they would open and play an m3u file in Windows Media Player. Basically an m3u file as I found out is just a listing of file locations that a media program uses to then create a playlist of songs. Here is what the one for Ehren Starks looks like.

http://he3.magnatune.com/all/01-Lines%20Build%20Walls-Ehren%20Starks.mp3

http://he3.magnatune.com/all/02-Amnesia-Ehren%20Starks.mp3

http://he3.magnatune.com/all/03-No%20One%20Will%20Ever%20Know-Ehren%20Starks.mp3

http://he3.magnatune.com/all/04-Paper%20Lights-Ehren%20Starks.mp3

http://he3.magnatune.com/all/05-You%20are%20the%20You-Ehren%20Starks.mp3

http://he3.magnatune.com/all/06-No%20Silence%20Please-Ehren%20Starks.mp3

http://he3.magnatune.com/all/07-Run-Ehren%20Starks.mp3

http://he3.magnatune.com/all/08-New%20Year's%20Love-Ehren%20Starks.mp3

http://he3.magnatune.com/all/09-One%20Big%20Hope-Ehren%20Starks.mp3

http://he3.magnatune.com/all/10-Tunnel%20Systems-Ehren%20Starks.mp3

http://he3.magnatune.com/all/11-Leaving%20the%20Theatre-Ehren%20Starks.mp3

So as you can see, this is just a list of files. Nothing complex so I set about writing an application that could read the list and download each file and save the file to a specified location. I just needed something quick and dirty to do this for me with a bunch of files so I created this application real quick to do that. I have posted a zip file that contains all of the code for the application and if you don’t want to compile it, the exe is under the bin\debug folder in the zip file.


The top textbox, the user selects the file from the browse menu. In the next textbox, the user selects a folder for the files from the download to be placed into. The status label will tell the user what file is currently being downloaded and the success label will tell the user how many files have been downloaded so far. The begin download button obviously starts the downloads but starts them on a separate thread so as not to lockup the drawing of the UI.

Setting up the browse buttons is fairly simple. Just drag an OpenFileDialog and a FolderBrowserDialog onto the designer view and those two objects will be created. Then add the buttons to the UI and name them. Then double click each button which will create a click event for each one and add the following code.

private void _btnBrowse_Click(object sender, EventArgs e)

{

_openFile.ShowDialog();

// Textbox with the location of the m3u file

_txtM3UFileLocation.Text = _openFile.FileName;

}

private void _btnFolderBrowser_Click(object sender, EventArgs e)

{

_folderBrowserDialog1.ShowDialog();

// Textbox with the folder location.

_txtdownloadLocation.Text = _folderBrowserDialog1.SelectedPath;

}

At the top of your Form1.cs file, make sure that you include all of these libraries.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.IO;

using System.Web;

using System.Net;

using System.Threading;

Now double click the “Begin Download” button in design view which will once again create another event. Since the button has been clicked, the user shouldn’t be able to click it again because the download process will have started so we disable it immediately. Here is the entire event. I will explain what it all does next and why. Notice though that stringArray and filePath are outside of the event. These are class variables.

String[] stringArray;

String filePath;

private void _btnBeginDownload_Click(object sender, EventArgs e)

{

// The button shouldn't be able to be pressed again once the thread starts running.

_btnBeginDownload.Enabled = false;

// Open the stream.

TextReader reader = new StreamReader(_txtM3UFileLocation.Text);

// Get the text from the file. Each line will be an address to a file.

String document = reader.ReadToEnd();

// These are all of the addresses.

stringArray = document.Split('\n');

filePath = _txtdownloadLocation.Text;

Thread fileThread = new Thread(new ThreadStart(MoveFiles));

fileThread.Start();

}

After the button is disabled, we open a TextReader object. The TextReader uses the StreamReader object which is set to the file location of the m3u file. We then read the file into a String object so that we can work with it easier.

Now we know that each line of the m3u file is a different http address. Note that sometimes though it doesn’t have to be and can be a comment or other information that starts with “#” but if it does, we’ll catch that later. For now, we’ll just put every line into the stringArray variable. We also will want to save the download location so that it can’t be changed once the application is running which would break the rest of the downloads if a bad value was placed in there. The application wouldn’t crash though.

Now, if you’re not familiar with threads, I’m not really going to explain them but that’s a pretty basic way that I show to start a thread. I’m not doing anything too complex there. “MoveFiles” is a function that I have defined below the above function which I will now present but if you want more of an introduction to basic worker threads, a good article is this one at The Code Project by Alex Fr.

private void MoveFiles()

{

int successCount = 0;

foreach (String location in stringArray)

{

try

{

// We need to pull the filename off of the end of the full file path. The filename

// will follow the last '/' character.

String[] parts = location.Split('/');

String fileName = parts[parts.Length - 1];

// Provide a status on the download.

SetText("Now moving " + fileName);

String saveFileLocation = filePath + "\\" + fileName;

Uri uriLocation = new Uri(location);

// Download file.

WebClient client = new WebClient();

client.DownloadFile(uriLocation, saveFileLocation);

// If the download succeded, increment the count.

successCount = successCount++;

SetCount(successCount.ToString());

}

catch

{

// If the line isn't a valid file, it'll just skip it.

// There is a better way to catch this but it's far simpler this way.

}

}

// Make sure we can run this again once the thread ends.

SetBeginDownload(true);

}

// These delegates and the functions below are used to safely call outside of the thread.

public delegate void SetBeginDownloadCallback(bool enabled);

public delegate void SetCountCallback(String text);

public delegate void SetTextCallback(String text);

private void SetBeginDownload(bool enabled)

{

if (this._lblStatus.InvokeRequired)

{

SetBeginDownloadCallback d = new SetBeginDownloadCallback(SetBeginDownload);

this.Invoke(d, new object[] { enabled });

}

else

{

this._btnBeginDownload.Enabled = enabled;

}

}

// Success count label.

private void SetCount(string text)

{

if (this._lblStatus.InvokeRequired)

{

SetCountCallback d = new SetCountCallback(SetCount);

this.Invoke(d, new object[] {text});

}

else

{

this._lblSuccessCount.Text = text;

}

}

// Set the status label.

private void SetText(string text)

{

if (this._lblStatus.InvokeRequired)

{

SetTextCallback d = new SetTextCallback(SetText);

this.Invoke(d, new object[] {text});

}

else

{

this._lblStatus.Text = text;

}

}

I’m sure that you can see in the code from the comments what is happening. For each file that I’m going to download I have to first call SetText() which is a function that allows this new thread function to call to the UI thread and change a value. SetText(), SetStatus() and SetBeginDownload() all allow for safe thread object access. If we were to just call the objects without using the invoke we could have memory access errors.

The next part takes the filename that we’re currently working on and pulls off the filename of the file. Now that we have the filename, we’ll use that to set where to save the file and what to name it when it’s created from the move. If client.DownloadFile doesn’t generate an exception, then the file was saved successfully and we can exit the loop or continue. Once they’re all finished, we set the BeginDownload button to be enabled again so that another file can be downloaded.

 
Comments:
You should compile this as a Windows executable and post it here... would be a really useful program for those of us who don't know what to do with your source code.
 
agreed with above!
 
Post a Comment





<< Home
I am currently an ASP.NET, C# developer working on MangosteenNation.com, a XanGo website for helping people build their businesses. I am also pursuing a degree at CSU San Marcos in Southern California.

XanGo at Mangosteen Nation

Archives
October 2005 / November 2005 / December 2005 / January 2006 / February 2006 / March 2006 / April 2006 / May 2006 / June 2006 / July 2006 / August 2006 / September 2006 / October 2006 / November 2006 / December 2006 / January 2007 / April 2007 / May 2007 / June 2007 / August 2007 / February 2008 / August 2008 /


Powered by Blogger

Subscribe to
Posts [Atom]