Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Thursday, 22 December 2011

In my exploration of Open Source projects of interest I came across this one.

http://www.hanselman.com/blog/RemovingDeadTracksDuplicatesThatDontExistFromITunesUsingC.aspx

image

So I think iTunes actually does find duplicates now Smile  .. but interesting code:

 public partial class Form1 : Form
    {
        private volatile bool _shouldStop;
“Volatile: Over-simplifying and paraphrasing:
volatile indicates that every read operation needs to re-read from memory because there might be other threads updating the variable.”
http://stackoverflow.com/questions/1186515/interlocked-and-volatile
Maybe dropped in .NET5? http://www.bluebytesoftware.com/blog/2010/12/04/SayonaraVolatile.aspx

Architecture

All in code behind the winform.

ListView with columns

Label1 and a Progress Bar.

checkBoxRemove

 

Worker is on a different thread to the UI. and is spun up when the button is pressed:

//button1 is find dead tracks
        private void button1_Click(object sender, EventArgs e)
        {
            this._shouldStop = false;
            this.buttonCancel.Enabled = true;
            this.listView1.Items.Clear();

            this.worker = new Thread(this.FindDeadTracks);
            this.worker.Start();
        }
 

Helper methods for FindDeadTracks and RemoveDuplicates

SetupProgress(max value eg trackcount) – this sets up the ProgressBar (we’re on the worker thread so have to invoke back to UI).. which magically calls the SetupProgress again and sets the max value of the progress bar

in FindDeadTracks (worker thread)

//setup the progress control
this.SetupProgress(trackCount);

then in class level

delegate void SetupProgressCallback(int max);
private void SetupProgress(int max)
        {
            if (this.progressBar1.InvokeRequired)
            {
                SetupProgressCallback cb = new SetupProgressCallback(SetupProgress);
                this.Invoke(cb, new object[] { max });
            }
            else
            {
                this.progressBar1.Maximum = max;
                this.progressBar1.Minimum = 1;
                this.progressBar1.Step = 1;
                this.progressBar1.Value = 1;
            }
        }

nifty!  worker thread calls it first, which it then goes into the InvokeRequired section.  Then the delegate calls it.

 

IncrementProgress – both worker methods can call this (its already been setup).  Same as above.. just increments a step

UpdateLabel – updates label1

FindDeadTrack

goes through and looks for an empty filename or an exception on System.File.IO.Exists

Remove Duplicates

Dictionary for trackCollection.  where key string could be:  FarewellYngwie MalmsteenRising Force    notice there are spaces

var trackCollection = new Dictionary<string, IITTrack>();

The app will never add duplicates into the trackCollection

ArrayList for tracksToRemove

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Collections;
using iTunesLib;

namespace iTunesCOMSample
{
    public partial class Form1 : Form
    {
        private volatile bool _shouldStop;
        private Thread worker;

        public Form1()
        {
            InitializeComponent();
        }
        
        private void Form1_Load(object sender, EventArgs e)
        {
            this.label1.Text = "";
            this.buttonCancel.Enabled = false;
        }

        private void RemoveDuplicates()
        {
            //create a reference to iTunes
            var iTunes = new iTunesAppClass();

            //get a reference to the collection of all tracks
            IITTrackCollection tracks = iTunes.LibraryPlaylist.Tracks;

            int trackCount = tracks.Count;
            int numberChecked = 0;
            int numberDuplicateFound = 0;
            //dictionary is key,value
            var trackCollection = new Dictionary<string, IITTrack>();
            var tracksToRemove = new ArrayList();

            //setup the progress control
            this.SetupProgress(trackCount);

            for (int i = trackCount; i > 0; i--)
            {
                if (tracks[i].Kind == ITTrackKind.ITTrackKindFile)
                {
                    if (!this._shouldStop)
                    {
                        numberChecked++;
                        this.IncrementProgress();
                        this.UpdateLabel("Checking track # " + numberChecked.ToString() + " - " + tracks[i].Name);
                        //eg key: FarewellYngwie MalmsteenRising Force"  notice there are spaces

                        string trackKey = tracks[i].Name + tracks[i].Artist + tracks[i].Album;

                        if (!trackCollection.ContainsKey(trackKey))
                            trackCollection.Add(trackKey, tracks[i]);
                        else //if the trackCollection does have this song already
                        {
                            //if not in the same album or not by the same artist?
                            if (trackCollection[trackKey].Album != tracks[i].Album || trackCollection[trackKey].Artist != tracks[i].Artist)
                                trackCollection.Add(trackKey, tracks[i]);
                            //if track in collection has a higherbitrate than current
                            else if (trackCollection[trackKey].BitRate > tracks[i].BitRate)
                            {
                                IITFileOrCDTrack fileTrack = (IITFileOrCDTrack)tracks[i];
                                numberDuplicateFound++;
                                tracksToRemove.Add(tracks[i]);
                            }
                            //bitrate is higher in this one so replace the one in trackCollection with this version.
                            //default just replace the existing track with this one
                            else
                            {
                                IITFileOrCDTrack fileTrack = (IITFileOrCDTrack)tracks[i];
                                trackCollection[trackKey] = fileTrack;
                                numberDuplicateFound++;
                                tracksToRemove.Add(tracks[i]);
                            }                            
                        }
                    }
                }                                
            }

            this.SetupProgress(tracksToRemove.Count);

            //tracksToRemove is an ArrayList
            for (int i = 0; i < tracksToRemove.Count; i++)
            {
                IITFileOrCDTrack track = (IITFileOrCDTrack)tracksToRemove[i];
                this.UpdateLabel("Removing " + track.Name);
                this.IncrementProgress();
                this.AddTrackToList((IITFileOrCDTrack)tracksToRemove[i]);

                if (this.checkBoxRemove.Checked)
                    track.Delete();
            }

            this.UpdateLabel("Checked " + numberChecked.ToString() + " tracks and " + numberDuplicateFound.ToString() + " duplicate tracks found.");
            this.SetupProgress(1);
        }

        private void FindDeadTracks()
        {
            //create a reference to iTunes
            iTunesAppClass iTunes = new iTunesAppClass();

            //get a reference to the collection of all tracks
            IITTrackCollection tracks = iTunes.LibraryPlaylist.Tracks;

            int trackCount = tracks.Count;
            int numberChecked = 0;
            int numberDeadFound = 0;

            //setup the progress control
            this.SetupProgress(trackCount);

            for (int i = trackCount; i > 0; i--)
            {
                if (!this._shouldStop)
                {
                    IITTrack track = tracks[i];
                    numberChecked++;
                    this.IncrementProgress();
                    this.UpdateLabel("Checking track # " + numberChecked.ToString() + " - " + track.Name);
                    
                    if (track.Kind == ITTrackKind.ITTrackKindFile)
                    {
                        IITFileOrCDTrack fileTrack = (IITFileOrCDTrack)track;                        

                        //if the file doesn't exist, we'll delete it from iTunes
                        if (fileTrack.Location == String.Empty)
                        {
                            numberDeadFound++;
                            this.AddTrackToList(fileTrack);

                            if (this.checkBoxRemove.Checked)
                                fileTrack.Delete();
                        }
                        else if (!System.IO.File.Exists(fileTrack.Location))
                        {
                            numberDeadFound++;
                            this.AddTrackToList(fileTrack);

                            if (this.checkBoxRemove.Checked)
                                fileTrack.Delete();
                        }
                    }
                }
            }

            this.UpdateLabel("Checked " + numberChecked.ToString() + " tracks and " + numberDeadFound.ToString() + " dead tracks found.");
            //sets the progressbar back to nothing
            this.SetupProgress(1);
        }

        #region Button clicks
        private void buttonCancel_Click(object sender, EventArgs e)
        {
            this._shouldStop = true;
            this.buttonCancel.Enabled = false;
        }

        //button1 is find dead tracks
        private void button1_Click(object sender, EventArgs e)
        {
            this._shouldStop = false;
            this.buttonCancel.Enabled = true;
            this.listView1.Items.Clear();

            this.worker = new Thread(this.FindDeadTracks);
            this.worker.Start();
        }

        //button2 is duplicates
        private void button2_Click(object sender, EventArgs e)
        {
            this._shouldStop = false;
            this.buttonCancel.Enabled = true;
            this.listView1.Items.Clear();

            this.worker = new Thread(this.RemoveDuplicates);
            this.worker.Start();
        }

        #endregion

        #region Delegate Callbacks
        //delagates for thread-safe access to UI components
        delegate void SetupProgressCallback(int max);
        delegate void IncrementProgressCallback();
        delegate void UpdateLabelCallback(string text);
        delegate void CompleteOperationCallback(string message);
        delegate void AddTrackToListCallback(IITFileOrCDTrack fileTrack);
        
        private void IncrementProgress()
        {
            if (this.progressBar1.InvokeRequired)
            {
                IncrementProgressCallback cb = new IncrementProgressCallback(IncrementProgress);
                this.Invoke(cb, new object[] { });
            }
            else
            {
                this.progressBar1.PerformStep();
            }
        }

        private void UpdateLabel(string text)
        {
            if (this.label1.InvokeRequired)
            {
                UpdateLabelCallback cb = new UpdateLabelCallback(UpdateLabel);
                this.Invoke(cb, new object[] { text });
            }
            else
            {
                this.label1.Text = text;
            }
        }

        private void CompleteOperation(string message)
        {
            if (this.label1.InvokeRequired)
            {
                CompleteOperationCallback cb = new CompleteOperationCallback(CompleteOperation);
                this.Invoke(cb, new object[] { message });
            }
            else
            {
                this.label1.Text = message;
            }
        }

        private void AddTrackToList(IITFileOrCDTrack fileTrack)
        {
            if (this.listView1.InvokeRequired)
            {
                AddTrackToListCallback cb = new AddTrackToListCallback(AddTrackToList);
                this.Invoke(cb, new object[] { fileTrack });
            }
            else
            {
                this.listView1.Items.Add(new ListViewItem(new string[] { fileTrack.Name, fileTrack.Artist, fileTrack.Location, fileTrack.BitRate.ToString() }));
            }
        }

        private void SetupProgress(int max)
        {
            if (this.progressBar1.InvokeRequired)
            {
                SetupProgressCallback cb = new SetupProgressCallback(SetupProgress);
                this.Invoke(cb, new object[] { max });
            }
            else
            {
                this.progressBar1.Maximum = max;
                this.progressBar1.Minimum = 1;
                this.progressBar1.Step = 1;
                this.progressBar1.Value = 1;
            }
        }
        #endregion

    }
}
| | #