Click here to Skip to main content
15,881,027 members
Articles / Programming Languages / C++

Extracting iTunes Data and Responding to its Events using iTunes SDK and C++

Rate me:
Please Sign up or sign in to vote.
4.64/5 (5 votes)
28 Aug 2006CPOL2 min read 54.8K   693   26   10
An article to show how iTunes SDK can be used to get all the songs and playlists of iTunes, getting Art work and getting the event notification from iTunes
Sample Image - iTunes_interact.jpg

Introduction

Around a year ago, I was writing a plug-in for one Media related application which extracts all the media items, meta-data and play lists from iTunes. The plug-in also responds to the events of iTunes like its database change due to addition/deletion of media item to/from iTunes. In this article, a very simple application is presented which loads all the data from iTunes by using the iTunes SDK and responds to its database change event by reloading all the data again. At that time not much resources were available on the internet dealing with the iTunes SDK, but I was not able to publish this article due to some non-technical issues. Some days back, I found that there are still not many resources available on CodeProject which deal with this iTunes SDK in C++. Most of the articles relating to iTunes SDK deal with C#, so I decided to write one article on this topic though it is very late.

The sample given here was originally written with iTunes version 5. I've tested it with the latest iTunes (ver 6.0.5.20) and it is working fine.

How to Use the Demo

You need to install iTunes, if it is not installed and add some media item to it. You'll get all the media items of iTunes in the sample and Artwork will be shown if the item is clicked, if any artwork is available. If you add/remove some item from the iTunes, the sample will also get updated.

Using the Code

All the media items added to iTunes come under Library tab. The loading of iTunes media item can be done in two ways. We can either load all the media items through Library or we can load it by play lists. In the sample, all data have been loaded through library. Here is the code snippet:

C++
CoInitialize(NULL);
    HRESULT hr;
    CComPtr<IITUNES> spITunesApp;
    
    // note - CLSID_iTunesApp and IID_IiTunes are defined in iTunesCOMInterface_i.c
    //create iTunes component
     hr = ::CoCreateInstance(CLSID_iTunesApp, 
                                    NULL, CLSCTX_LOCAL_SERVER,//local server 
                                    __uuidof(spITunesApp), 
                                    (void **)&spITunesApp);    

    if(FAILED(hr))
    {
        AfxMessageBox("Failed to load iTunes Component, is iTunes installed????");
        return;
    }

#if 1          //connection point to receive database change notification
    IConnectionPointContainer  * pCPC;

    hr=spITunesApp->QueryInterface(IID_IConnectionPointContainer,(void **)&pCPC);

    IConnectionPoint * pCP;

    _IiTunesEvents * pITEvent;
    hr=pCPC->FindConnectionPoint (__uuidof(pITEvent),&pCP);
    pCPC->Release();

    IUnknown *pSinkUnk;
    CITunesEventSink * pSink;
    pSink=new CITunesEventSink;
    hr = pSink->QueryInterface (IID_IUnknown,(void **)&pSinkUnk);

    DWORD dwAdvise;
    hr = pCP->Advise(pSinkUnk,&dwAdvise); 

#endif

    CComPtr <IITSOURCECOLLECTION>  spSourceCollection;

    //check whether iTunes is already executing
    //if it is not executing then we'll hide it
    BOOL bRes=IsITunesNeedsToBeHidden();
    HANDLE hThread;

    //
    //itunes pops up when we call 
    //the function of its component
    //so hide it by this patchy method...
    //
    
    if(bRes) //iTunes was not there so we need to hide it
    {
        //create auto reset events
        ghEvt[ThreadStarted]=CreateEvent(NULL,FALSE,FALSE,"ThreadStart");
        ghEvt[ThreadEnded]=CreateEvent(NULL,FALSE,FALSE,"ThreadEnd");

        //strat the thread to hide iTunes
        hThread=(HANDLE)_beginthread(ThreadHideITunes,0,NULL);
        //wait till the thread starts
        WaitForSingleObject(ghEvt[ThreadStarted],INFINITE);
        //get source collection
        hr=spITunesApp->get_Sources(&spSourceCollection); 
        if(FAILED(hr))
        {
            return(ErrorMessage(hr));
            
        }
        //stop iTunes hide thread
        SetEvent(ghEvt[ThreadEnded]);
        //wait till the thread stops
        WaitForSingleObject(hThread,INFINITE);
        //Now minimize iTunes and Hide
        ITunesMinimizeAndHide();
    }
    else //iTunes was there so need to hide do normal things
    {
        //get source collection
        hr=spITunesApp->get_Sources(&spSourceCollection); 
        if(FAILED(hr) || hr==S_FALSE)
        {
            return(ErrorMessage(hr));
        }
    }   
   
   long countSource;
   long countPlaylist;
   long countTracklist;
   
   //get the source count 
   hr=spSourceCollection->get_Count (&countSource);
   CComPtr<IITSOURCE>  spSource;
   CComPtr<IITPLAYLISTCOLLECTION> spPlaylistCollection;
   CComPtr<IITPLAYLIST> spPlaylist;
   CComPtr<IITTRACKCOLLECTION>  spTrackCollection; 
  
   CComPtr<IITARTWORK>  spAlbumArt;
   CComPtr<IITARTWORKCOLLECTION> spAlbumArtCollection;
   ITArtworkFormat artFormat;
   
   CComBSTR bstrName;
   CComBSTR bstrLocation;
   CComBSTR bstrArtist;
   CComBSTR bstrAlbum;
   CComBSTR bstrTime;
   CComBSTR bstrYear;
   CComBSTR bstrAlbumArtPath;
   char szAlbumArtPath[255];
   
   hr=spSourceCollection->get_Item (1,&spSource);
   if(SUCCEEDED(hr))
   {
       spSource->get_Playlists (&spPlaylistCollection);
       spPlaylistCollection->get_Count (&countPlaylist);
        
       hr=spPlaylistCollection->get_Item (1,&spPlaylist);
       spPlaylist->get_Tracks (&spTrackCollection);
       spTrackCollection->get_Count (&countTracklist);

       m_ListCtrl.DeleteAllItems ();

       for(long k=1;k<=countTracklist;k++)
       {
           CComPtr<IITTRACK>  spTrack;          
           
           hr=spTrackCollection->get_Item (k,&spTrack);
                  
           spTrack->get_Name (&bstrName);

           CComPtr<IITFILEORCDTRACK> spFileTrack;

           hr=spTrack->QueryInterface (__uuidof(spFileTrack),(void **)&spFileTrack);
           //Get the ArtWork
           hr=spTrack->get_Artwork (&spAlbumArtCollection);
           hr=spAlbumArtCollection->get_Item (1,&spAlbumArt);

           USES_CONVERSION;
           
           if(SUCCEEDED(hr) && hr!=S_FALSE)
           {
               hr=spAlbumArt->get_Format (&artFormat);
               
               if(SUCCEEDED(hr))
               {
                   CString strTemp=OLE2T(bstrName);
                   strTemp.TrimLeft ("\"");
                   strTemp.TrimRight ("\"");

                   sprintf(szAlbumArtPath,
		  "%s%s%s%d%s",m_szCurDir,"\\",strTemp.GetBuffer (),k,".");
                   
                   switch(artFormat)
                   {
                   case ITArtworkFormatJPEG:
                       strcat(szAlbumArtPath,"jpg");
                       bstrAlbumArtPath=T2OLE(szAlbumArtPath);
                       hr=spAlbumArt->SaveArtworkToFile (bstrAlbumArtPath);
                      break;
                   case ITArtworkFormatPNG :
                       strcat(szAlbumArtPath,"png");
                       bstrAlbumArtPath=T2OLE(szAlbumArtPath);
                       spAlbumArt->SaveArtworkToFile (bstrAlbumArtPath);
                       break;
                   case ITArtworkFormatBMP:
                       strcat(szAlbumArtPath,"bmp");
                       bstrAlbumArtPath=T2OLE(szAlbumArtPath);
                       spAlbumArt->SaveArtworkToFile (bstrAlbumArtPath);
                       break;
                   default:
                       break;
                   }
               }
           }

           spFileTrack->get_Location (&bstrLocation);
           spTrack->get_Artist (&bstrArtist);
           spTrack->get_Album (&bstrAlbum);
           spTrack->get_Time (&bstrTime);
           // pTrack->get_Year (&bstrYear);
                  
           LV_ITEM item;
           item.mask = LVIF_TEXT | LVIF_IMAGE;
           item.iItem = m_ListCtrl.GetItemCount();
           item.iImage = (k-1) % 5;
           item.iSubItem = 0;
           item.cchTextMax = 255;
                 
           item.pszText = OLE2T(bstrName);
           m_ListCtrl.InsertItem (&item);

           item.pszText = OLE2T(bstrLocation);
           item.iSubItem = 1;
           m_ListCtrl.SetItem(&item);

           item.pszText = OLE2T(bstrArtist);
           item.iSubItem = 2;
           m_ListCtrl.SetItem(&item);

           item.pszText = OLE2T(bstrAlbum);
           item.iSubItem = 3;
           m_ListCtrl.SetItem(&item);

           item.pszText = OLE2T(bstrTime);
           item.iSubItem = 4;
           m_ListCtrl.SetItem(&item);
       }
   }

//   AfxMessageBox("Now going to show Unsmart Playlist items....");
//   UnsmartListItems(pITunes);

   CoUninitialize();

The play list wise media item loading is also shown in the function UnsmartListItems(IiTunes *pITunes).

C++
void UnsmartListItems(IiTunes *pITunes)
{
    long lSourceID;
    long lPlaylistID;
    long lTrackId;
    long countSource;
    
    IITObject * pObject;
//    IITUserPlaylist * pUserPlaylist;
    IITSourceCollection * pCollection;      
       
    USES_CONVERSION;       

    short bIsSmart=0;

    HRESULT hr=pITunes->get_Sources(&pCollection); 
    hr=pCollection->get_Count (&countSource);

    IITSource * pSource;
    IITPlaylistCollection * pPlaylistCollection;
    IITPlaylist * pPlaylist;
    IITTrackCollection * pTrackCollection;
    IITTrack * pTrack;

    CComPtr<IITFILEORCDTRACK> fileTrack;

    long countPlaylist;
    long countTracklist;
    CComBSTR bstrName;

    for(long i=1;i<=countSource;i++)
    {
        hr=pCollection->get_Item (i,&pSource);
        //
        pSource->get_SourceID (&lSourceID);
        pSource->get_Playlists (&pPlaylistCollection);
        pPlaylistCollection->get_Count (&countPlaylist);
        for(long j=1;j<=countPlaylist;j++)
        {
            hr=pPlaylistCollection->get_Item (j,&pPlaylist);
            pPlaylist->get_PlaylistID (&lPlaylistID);            
              
            hr=pITunes->GetITObjectByID (lSourceID,lPlaylistID,0,0,&pObject);
            pObject->get_Name (&bstrName);

            CComPtr<IITUSERPLAYLIST> spUserPlaylist; 
            hr=pPlaylist->QueryInterface(&spUserPlaylist);

            if(SUCCEEDED(hr))
            {
                spUserPlaylist->get_Smart(&bIsSmart);
                if(bIsSmart==0)
                {
                    ITUserPlaylistSpecialKind  plKind;
                    spUserPlaylist->get_SpecialKind (&plKind);
                    if(plKind==ITUserPlaylistSpecialKindPodcasts)
                    {
                        AfxMessageBox(OLE2T(bstrName));
                        pPlaylist->get_Tracks (&pTrackCollection);
                        pTrackCollection->get_Count (&countTracklist);

                        //m_ListCtrl.DeleteAllItems ();
                
                        for(long k=1;k<=countTracklist;k++)
                        {
                            hr=pTrackCollection->get_Item (k,&pTrack);
                            pTrack->get_TrackID(&lTrackId);
                            pTrack->get_Name (&bstrName);
                            AfxMessageBox(OLE2T(bstrName));
                        }
                    }
                }                
            }
        }
    }
}

Points of Interest

In the sample, all the media items load code has been called from the main thread of the application. It should be put in a separate worker thread. It has just been put here for simplicity.

One pesky thing about the Tunes SDK is that whenever you call any of the iTunes component's methods, the iTunes UI will pop up. I couldn't find any way to block it, so in this sample iTunes has been hidden in a patchy way which results in a flicker in slow PCs.

Conclusion

I hope you guys find this sample helpful when you are going to interact with iTunes SDK. You can contact me, if you have anything to ask.

Credits/Acknowledgements

This sample uses code from two articles on CodeProject:

  1. Alternate row coloring CListCtrl by sdancer75, George Papaioannou
  2. WallpaperQ by Jason Henderson, for previewing album art in thumbnails

History

  • 28th August, 2006: Initial post 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India
Anindya Sengupta is a Software Developer from the eastern part of India, working on C/C++/VC++/MFC/COM/ATL/DirectX. When he is not writing code he may be listening to some songs of R.E.M or Green Day.

Comments and Discussions

 
QuestionGetting Event Parameters Pin
dshiells27-Nov-13 1:47
dshiells27-Nov-13 1:47 
GeneralUsing the article example for a simple non MFC script Pin
pardillojuegos29-Jun-12 6:27
pardillojuegos29-Jun-12 6:27 
GeneralExtracting data from iPhone using iTunes Pin
champ231-Apr-10 1:26
champ231-Apr-10 1:26 
GeneralCoverart Doesn't Work Pin
MicealG31-Aug-07 0:33
MicealG31-Aug-07 0:33 
GeneralFolders... Pin
Mike Rabtok25-Oct-06 2:28
Mike Rabtok25-Oct-06 2:28 
GeneralRe: Folders... Pin
anindya_sengupta26-Oct-06 0:38
anindya_sengupta26-Oct-06 0:38 
GeneralRe: Folders... Pin
cziy12225-Nov-09 4:21
cziy12225-Nov-09 4:21 
GeneraliTunes Listview Pin
Bruno_Bieri29-Sep-06 1:42
Bruno_Bieri29-Sep-06 1:42 
GeneralRe: iTunes Listview Pin
anindya_sengupta2-Oct-06 20:48
anindya_sengupta2-Oct-06 20:48 
GeneralRe: iTunes Listview Pin
Bruno_Bieri11-Nov-07 5:51
Bruno_Bieri11-Nov-07 5:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.