Moving iTunes Library in two steps using COM interface and RoboCopy

Several years ago I migrated from storing my data on on server to another. I had all of my music stored in an hierarchy under the UNC sharename of \\Via\Music and moved it to a different server with the sharename of \\FortMeade\Music.

My music collection is around 60 gigabytes, and my experience with apple software in windows has not been very good. Telling the itunes application itself to do the moving did not seem like the best of plans if I wanted to be able to use my computer any time soon.

I decided that the most reliable method of transfering the files was to use the robocopy command and the windows command prompt. This took several hours over gigabit ethernet but got the data copied over.

I wanted to maintain as much of the data as possible about the individual tracks, and decided I could write a program using the COM interface to control iTunes and copy the entries from the old tracks and put them in for the new location.

I wrote a program in C++ using Microsoft Visual Studio, and successfully maintained the last played date and the number of plays. Unfortunately the location data of a track seems to be read only, otherwise I’d simply have changed that and been able to maintain all of the data.

Several years later, and I want to move to another server. Now when I modify my program and attempt to run it with the new names, it adds the tracks in the new location, but when it tries to modify the PlayedCount value, it traps a COM error (0xA0040203), and the error doesn’t seem to have a description.


// iManipulate.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "iManipulate.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#undef GetFreeSpace /* macros are evil */
#import "C:/Program Files (x86)/iTunes/iTunes.exe"
using namespace iTunesLib;

// The one and only application object
CWinApp theApp;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;
  // initialize MFC and print and error on failure
  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
  {
    _tprintf(_T("Fatal Error: MFC initialization failed\n"));
    nRetCode = 1;
  }
  else
  {
    // initialize COM libraries
    HRESULT hr = CoInitialize(NULL);
    if (SUCCEEDED(hr))
    {
      CComPtr<IiTunes> iTunes;
      if (S_OK == iTunes.CoCreateInstance(__uuidof(iTunesApp)))
      {
        CComPtr<IITLibraryPlaylist> MyLibrary = iTunes->LibraryPlaylist;
        CComPtr<IITTrackCollection> MyTrackCollection = MyLibrary->Tracks;
        for (long index = 1; index <= MyTrackCollection->Count ; index++)
        {
          CComPtr<IITTrack> MyTrack = MyTrackCollection->Item[index];
          if (MyTrack->Kind == ITTrackKindFile)
          {
            CComPtr<IITFileOrCDTrack> MyFileOrCDTrack;
            if (S_OK == MyTrack->QueryInterface(&MyFileOrCDTrack))
            {
              _tprintf(_T("%d"), index);
              BSTR MyTrackLocation = MyFileOrCDTrack->Location;
              _tprintf(_T("\t%s"), MyTrackLocation);
              CString NewLocation(MyTrackLocation);
              CString NewLocationAllLowerCase(NewLocation);
              NewLocationAllLowerCase.MakeLower();
              CString TextToReplace(_T("\\\\fortmeade\\music"));
              CString NewText(_T("\\\\Acid\\Music"));
              int ReplaceStart = NewLocationAllLowerCase.Find(TextToReplace);
              if (ReplaceStart >= 0)
              {
                NewLocation.Delete(ReplaceStart,TextToReplace.GetLength());
                NewLocation.Insert(0,NewText);
               if (-1 != _taccess(NewLocation,0))
               {
                 try
                 {
                   CComBSTR FileToAdd(NewLocation);
                   CComPtr<IITOperationStatus> MyOperationStatus = MyLibrary->AddFile(FileToAdd.m_str);
                   while ( VARIANT_TRUE == MyOperationStatus->GetInProgress())
                     Sleep(100);
                   CComPtr<IITTrackCollection> MyAddedTrackCollection = MyOperationStatus->GetTracks();
                   if (1 == MyAddedTrackCollection->Count)
                   {
                     CComPtr<IITTrack> MyAddedTrack = MyAddedTrackCollection->GetItem(1);
                     //MyAddedTrack->DateAdded = MyFileOrCDTrack->DateAdded;
                     if (MyFileOrCDTrack->Rating > 0)
                       MyAddedTrack->PutRating(MyFileOrCDTrack->Rating);
                     if (MyFileOrCDTrack->PlayedCount > 0)
                     {
                       MyAddedTrack->PutPlayedCount(MyFileOrCDTrack->GetPlayedCount());
                       MyAddedTrack->PutPlayedDate(MyFileOrCDTrack->GetPlayedDate());
                     }
                     MyFileOrCDTrack->Delete();
                     _tprintf(_T("\t%s\n"), FileToAdd);
                   }
                 }
                 catch(_com_error &e)
                 {
                   _tprintf(_T("\t(COM ERROR:%#lx %s, %s)\n"), e.Error(), e.Source(), e.Description());
                 }
               }
             }
             else
               _tprintf(_T("\r"));
           }
         }
       }
     }
   }
   CoUninitialize();
 }
 return nRetCode;
}
Advertisements

4 thoughts on “Moving iTunes Library in two steps using COM interface and RoboCopy

  1. I had the same problem you mention at the end: it turned out that I was trying to update the play count too soon after adding the track to the iTunes library. As soon as you add the track, iTunes goes through its “Determining audio volume” process, which takes maybe 15 seconds for a podcast track, during which time it locks the track (which apparently is what the COM error means). A little loop to retry after a suitable wait got round this. (Determining audio volume is done if you’ve set the relevant Preference somewhere)

  2. I was hoping that the two lines:

    while ( VARIANT_TRUE == MyOperationStatus->GetInProgress())
    Sleep(100);

    would take care of the delay on adding the new track into the library. It seemed to fix my problem when I originally wrote the program.

    • If I remember correctly, the “in progress” status applies only to the import operation (e.g. during a format conversion), but then iTunes does the audio level check after signalling completion of the import, so you have to wait until it unlocks the file. Maybe you changed the “Sound check” setting after you used the program way back when, hence the change in behaviour..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s