Logo Search packages:      
Sourcecode: zinf version File versions  Download package

musiccatalog.cpp

/*____________________________________________________________________________

        Zinf - Zinf Is Not FreeA*p (The Free MP3 Player)

        Portions Copyright (C) 1999 EMusic.com

        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 2 of the License, or
        (at your option) any later version.

        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.

        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

        $Id: musiccatalog.cpp,v 1.18 2004/02/02 18:34:19 enxrah Exp $
____________________________________________________________________________*/

// The debugger can't handle symbols more than 255 characters long.
// STL often creates symbols longer than that.
// When symbols are longer than 255 characters, the warning is disabled.
#include "config.h"
#ifdef WIN32
#pragma warning(disable:4786) 
#endif

#include <sys/stat.h>

#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#include "win32impl.h"
#endif

#include <string>
#include <algorithm>

using namespace std;

#include "path_max.h"

#include "musiccatalog.h"
#include "player.h"
#include "utility.h"
#include "pmo.h"
#include "debug.h"

#define METADATABASE_VERSION 1
#define DATABASE_SUB_VERSION 3

MusicCatalog::MusicCatalog(FAContext *context, char *databasepath)
{
    m_database = NULL;
    m_context = context;
    m_mutex = new Mutex();
    m_catMutex = new Mutex();
    m_timerMutex = new Mutex();
    m_MBLookupLock = new Mutex();
    m_inUpdateSong = false;
    m_addImmediately = false;
    m_bSurpressAddMessages = false;
    m_trackCount = 0;
    m_artistList = new vector<ArtistList *>;
    m_unsorted = new vector<PlaylistItem *>;
    m_playlists = new vector<string>;
    m_streams = new vector<PlaylistItem *>;
    m_MBRequests = new vector<pair<string, string> >;
    m_watchTimer = NULL;

    m_killGUIDs = false;

    m_killMBThread = false;
    m_MBLookupThreadActive = false;
    m_pendingMBLookups = 0;
    m_timeout = 0;
    context->prefs->GetPrefInt32(kWatchThisDirTimeoutPref, &m_timeout);

    if (databasepath)
        SetDatabase(databasepath);
}

void MusicCatalog::StartTimer(void)
{
    if (m_timeout > 0) {
        WatchTimer();
        m_context->timerManager->StartTimer(&m_watchTimer, watch_timer,
                                            m_timeout, this);
    }
}

MusicCatalog::~MusicCatalog()
{
    m_killMBThread = true;
    while (m_MBLookupThreadActive)
        usleep(50);

    ClearCatalog();
    StopSearchMusic();

    m_mutex->Acquire();
    m_catMutex->Acquire();
    m_timerMutex->Acquire();

    if(m_watchTimer)
        m_context->timerManager->StopTimer(m_watchTimer);

    delete m_artistList;
    delete m_unsorted;
    delete m_streams;

    if (m_database)
        delete m_database;

    delete m_playlists;
    delete m_mutex;
    delete m_catMutex;
    delete m_timerMutex;
}

class comp_catalog {
  public:
      bool operator()(PlaylistItem *a, PlaylistItem *b)
      {
          if (a->GetMetaData().Track() == b->GetMetaData().Track())
             // sort alphabetically...
             return (a->GetMetaData().Title() < b->GetMetaData().Title());
          return (a->GetMetaData().Track() < b->GetMetaData().Track());
      }
      bool operator()(AlbumList *a, AlbumList *b)
      {
          return (a->name < b->name);
      }
      bool operator()(ArtistList *a, ArtistList *b)
      {
          return (a->name < b->name);
      }
};

class nocase_comp_catalog {
  public:
      bool operator()(PlaylistItem *a, PlaylistItem *b)
      {
          if (a->GetMetaData().Track() == b->GetMetaData().Track())
             // sort alphabetically...
             return (a->GetMetaData().Title() < b->GetMetaData().Title());
          return (a->GetMetaData().Track() < b->GetMetaData().Track());
      }
      bool operator()(AlbumList *a, AlbumList *b)
      {
          return strcasecmp(a->name.c_str(), b->name.c_str()) < 0;
      }
      bool operator()(ArtistList *a, ArtistList *b)
      {
          return strcasecmp(a->name.c_str(), b->name.c_str()) < 0;
      }
};

void MusicCatalog::Sort(void)
{
    m_catMutex->Acquire();
    // Sort the playlists...
    sort(m_playlists->begin(), m_playlists->end());

    // Sort the uncategorized tracks
    sort(m_unsorted->begin(), m_unsorted->end(), comp_catalog());

    // Sort the streams
    sort(m_streams->begin(), m_streams->end(), comp_catalog());

    m_catMutex->Release();
}

Error MusicCatalog::RemovePlaylist(const char *url)
{
    assert(url);

    if (!m_database->Working())
        return kError_DatabaseNotWorking;

    char *data = m_database->Value(url);

    if (!data)
        return kError_ItemNotFound;

    delete [] data;
    m_database->Remove(url);

    m_catMutex->Acquire();

    vector<string>::iterator i = m_playlists->begin();
    for (; i != m_playlists->end(); i++)
         if ((*i) == url) {
             string tempstr = url;
             if (tempstr.find("currentlist.m3u") >= tempstr.length()) {
                 string sUrl = url; 
                 m_context->target->AcceptEvent(new MusicCatalogPlaylistRemovedEvent(sUrl));
                 m_playlists->erase(i);
             }
             m_catMutex->Release();
             return kError_NoErr;
         }   

    m_catMutex->Release();
    return kError_ItemNotFound;
}


Error MusicCatalog::AddPlaylist(const char *url)
{
    assert(url);

    if (!m_database->Working())
        return kError_DatabaseNotWorking;
    
    char *data = m_database->Value(url);
    if (!data) 
        m_database->Insert(url, "P");
    else 
        delete [] data;

    bool found = false;
    m_catMutex->Acquire();
    vector<string>::iterator i = m_playlists->begin();
    for (; i != m_playlists->end(); i++)
         if ((*i) == url)
             found = true;
    
    if (!found) {
        string tempstr = url;
        if (tempstr.find("currentlist.m3u") < tempstr.length()) 
            m_playlists->insert(m_playlists->begin(), url);
        else
            m_playlists->push_back(url);

        string sUrl = url;
        m_context->target->AcceptEvent(new MusicCatalogPlaylistAddedEvent(sUrl));
        m_catMutex->Release();
        return kError_NoErr;
    }

    m_catMutex->Release();
    return kError_DuplicateItem;
}

Error MusicCatalog::RemoveStream(const char *url)
{
    assert(url);
    m_catMutex->Acquire();
    if (!m_database->Working()){
      m_catMutex->Release();
        return kError_DatabaseNotWorking;
    }
    MetaData *meta = ReadMetaDataFromDatabase(url);
    if (!meta){
      m_catMutex->Release();
        return kError_ItemNotFound;
    }
    string strGUID = meta->GUID();
    m_database->Remove(url);
    vector<PlaylistItem *>::iterator i = m_streams->begin();
    for (; i != m_streams->end(); i++)
         if ((*i)->URL() == url) {
             m_context->target->AcceptEvent(new MusicCatalogStreamRemovedEvent(*i));
             delete (*i);
             m_streams->erase(i);
             break;
         }
    delete meta;
    m_catMutex->Release();
    return kError_NoErr;
}

PlaylistItem *MusicCatalog::GetPlaylistItemFromURL(const char *url)
{
    assert(url);
    PlaylistItem *retitem = NULL;

    if (!m_database->Working())
        return retitem;

    MetaData *meta = ReadMetaDataFromDatabase(url);
    if (!meta)
        return retitem;

    m_catMutex->Acquire();
    if ((meta->Artist().size() == 0) || (meta->Artist() == " ")) {
        vector<PlaylistItem *>::iterator i = m_unsorted->begin();
        for (; i != m_unsorted->end(); i++)
            if ((*i)->URL() == url)
            {
                retitem = *i;
                break;
            }
    }
    else 
    {
        vector<ArtistList *>::iterator artist;
        if(FindArtistList(meta->Artist(), artist))
            {
            vector<AlbumList *> *alList = (*artist)->m_albumList;
            vector<AlbumList *>::iterator album;
            if(FindAlbumList(meta->Album(), alList, album))
                {
                vector<PlaylistItem *> *trList = (*album)->m_trackList;
                vector<PlaylistItem *>::iterator k;
                if(FindPlaylistItemByUrl(url, trList, k))
                            {
                                retitem = *k;
                }
            }
        }
    }
    delete meta;

    m_catMutex->Release();
    return retitem;
}

/**
 * Performs a find operation for the <code>ArtistList</code> entry
 * with the specified name. Returns <code>true</code> if found. The
 * iterator will be updated to the point to insert a new artist to
 * retain ordering (if <code>false</code> is returned), otherwise it
 * will point to the <code>ArtistList</code> found.
 */
bool MusicCatalog::FindArtistList(const std::string& artistName, std::vector<ArtistList*>::iterator& it) const
{
    ArtistList dummy; dummy.name = artistName;
    it = lower_bound(m_artistList->begin(), m_artistList->end(), &dummy, nocase_comp_catalog());
    return (it != m_artistList->end() && CaseCompare(artistName, (*it)->name));
}

bool MusicCatalog::FindAlbumList(const std::string& albumName, vector<AlbumList*>* albumList, std::vector<AlbumList*>::iterator& it) const
{
    AlbumList dummy; dummy.name = albumName;
    it = lower_bound(albumList->begin(), albumList->end(), &dummy, nocase_comp_catalog());
    return (it != albumList->end() && CaseCompare(albumName, (*it)->name));
}

bool MusicCatalog::FindPlaylistItemByUrl(const std::string& url, vector<PlaylistItem*>* trackList, vector<PlaylistItem*>::iterator& it) const
{
    for(it = trackList->begin(); it != trackList->end(); ++it)
    {
        if (url == (*it)->URL())
        {
            return true;
        } 
    }
    return false;
}

Error MusicCatalog::RemoveSong(const char *url)
{
    assert(url);

    if (!m_database->Working())
        return kError_DatabaseNotWorking;

    m_catMutex->Acquire();
    MetaData *meta = ReadMetaDataFromDatabase(url);
    if (!meta){
      m_catMutex->Release();
        return kError_ItemNotFound;
    }
    m_database->Remove(url);

    if ((meta->Artist().size() == 0) || (meta->Artist() == " ")) {
        vector<PlaylistItem *>::iterator i = m_unsorted->begin();
        for (; i != m_unsorted->end(); i++)
            if ((*i)->URL() == url) {
                AcceptEvent(new MusicCatalogTrackRemovedEvent(*i, NULL, NULL));
                m_unsorted->erase(i);
                m_trackCount--;
                break;
            }
    } else {
        vector<ArtistList *>::iterator artist;
        if(FindArtistList(meta->Artist(), artist)) {
            vector<AlbumList *> *alList = (*artist)->m_albumList;
            vector<AlbumList *>::iterator album;
            if(FindAlbumList(meta->Album(), alList, album)) {
                vector<PlaylistItem *> *trList = (*album)->m_trackList;
                vector<PlaylistItem *>::iterator k;
                if(FindPlaylistItemByUrl(url, trList, k)) {
                    AcceptEvent(new MusicCatalogTrackRemovedEvent(*k, *artist, *album));
                    trList->erase(k);
                    m_trackCount--;
                }    
                if (trList->size() == 0) {
                    alList->erase(album);
                }
            }
            if (alList->size() == 0) {
                m_artistList->erase(artist);
            }
        }
    }
    delete meta;
    m_catMutex->Release();
    return kError_NoErr;
}

Error MusicCatalog::AddStream(const char *url)
{
    assert(url);
    m_catMutex->Acquire();
    if (!m_database->Working()){
      m_catMutex->Release();
        return kError_DatabaseNotWorking;
    }
    PlaylistItem *newstream;
    MetaData *meta = ReadMetaDataFromDatabase(url);
    if (!meta){
      m_catMutex->Release();
        return kError_DatabaseNotWorking;
    }
    newstream = new PlaylistItem(url, meta);
    vector<PlaylistItem *>::iterator i = m_streams->begin();
    for (; i != m_streams->end(); i++) 
        if ((*i)->URL() == url){
          m_catMutex->Release();
            return kError_DuplicateItem;
      }
    m_streams->push_back(newstream);
    m_context->target->AcceptEvent(new MusicCatalogStreamAddedEvent(newstream));
    m_catMutex->Release();
    return kError_NoErr;
}

bool MusicCatalog::CaseCompare(const string &s1, const string &s2) const
{
    if (s1.length() == s2.length()) {
        for (uint32_t i = 0; i < s1.length(); i++) {
            if (toupper(s1[i]) != toupper(s2[i]))
                return false;
        }
        return true;
    }
    return false;
}

Error MusicCatalog::AddSong(const char *url)
{
    assert(url);
    m_catMutex->Acquire();
    if (!m_database->Working()){
      m_catMutex->Release();
        return kError_DatabaseNotWorking;
    }
    PlaylistItem *newtrack;
    MetaData *meta = ReadMetaDataFromDatabase(url);
    if (!meta) {
        newtrack = new PlaylistItem(url);
        m_context->plm->RetrieveMetaDataNow(newtrack);
        MetaData tempdata = (MetaData)(newtrack->GetMetaData());
        WriteMetaDataToDatabase(url, tempdata);
        delete newtrack;
        meta = ReadMetaDataFromDatabase(url);
    }
    if (!meta){
      m_catMutex->Release();
        return kError_DatabaseNotWorking;
    }
    if (m_killGUIDs){ 
        meta->SetGUID("");
        WriteMetaDataToDatabase(url, *meta);
    }
    newtrack = new PlaylistItem(url, meta);
    if ((meta->Artist().size() == 0) || (meta->Artist() == " ")) {
        vector<PlaylistItem *>::iterator i = m_unsorted->begin();
        for (; i != m_unsorted->end(); i++)
             if ((*i)->URL() == url) {
                 delete meta;
                 delete newtrack;
             m_catMutex->Release();
                 return kError_DuplicateItem;
             }
        m_unsorted->push_back(newtrack);
        AcceptEvent(new MusicCatalogTrackAddedEvent(newtrack, NULL, NULL));
        m_trackCount++;
    } else {
        bool changed = false;
        if (meta->Album() == " " || meta->Album().size() == 0) {
            string unknownstr = string(_("Unknown"));
            meta->SetAlbum(unknownstr);
            changed = true;
        }
        if (meta->Title() == " " || meta->Title().size() == 0) {
            string unknownstr = string(_("Unknown"));
            meta->SetTitle(unknownstr);
            changed = true;
        }
        if (changed)
            WriteMetaDataToDatabase(url, *meta);
        ArtistList* artist;
        AlbumList* album;
        vector<ArtistList *>::iterator artistInsert;
        if(FindArtistList(meta->Artist(), artistInsert)) {
            artist = *artistInsert;
            vector<AlbumList *>::iterator albumInsert;
            vector<AlbumList *> *alList = artist->m_albumList;
            if(FindAlbumList(meta->Album(), alList, albumInsert)) {
                album = *albumInsert;
                vector<PlaylistItem*> *trList = album->m_trackList;
                vector<PlaylistItem*>::iterator k;
                if(FindPlaylistItemByUrl(url, trList, k)) {
                                delete newtrack;
                                delete meta;  
                                m_catMutex->Release();
                                return kError_DuplicateItem;
                            }
                PlaylistItem dummy; dummy.SetMetaData(meta);
                vector<PlaylistItem*>::iterator trackInsert = lower_bound(trList->begin(), trList->end(), &dummy, nocase_comp_catalog());
                trList->insert(trackInsert, newtrack);
            } else {
                album = new AlbumList;
                album->name = meta->Album();
                album->m_trackList->push_back(newtrack);
                alList->insert(albumInsert, album);
            }
        } else {
            artist = new ArtistList;
            artist->name = meta->Artist();
            album = new AlbumList;
            album->name = meta->Album();
            album->m_trackList->push_back(newtrack);
            artist->m_albumList->push_back(album);
            m_artistList->insert(artistInsert, artist);
                        }
                        m_trackCount++;
        AcceptEvent(new MusicCatalogTrackAddedEvent(newtrack, artist, album));
    }
    delete meta;
    m_catMutex->Release();
    return kError_NoErr;
}

Error MusicCatalog::UpdateSong(PlaylistItem *item)
{
    assert(item);
    if (!m_database->Working())
        return kError_DatabaseNotWorking;
    m_inUpdateSong = true;
    Error err = RemoveSong(item->URL().c_str());    
    if (IsError(err))
        return err;
    WriteMetaDataToDatabase(item->URL(), item->GetMetaData());
    m_database->Sync();
    err = AddSong(item->URL().c_str());
    if (IsError(err))
        return err;
    m_inUpdateSong = false;
    m_context->target->AcceptEvent(new MusicCatalogTrackChangedEvent(m_oldItem, m_newItem, m_oldArtist, m_newArtist, m_oldAlbum, m_newAlbum));
    return kError_NoErr;
}

Error MusicCatalog::Remove(const char *url)
{
    assert(url);
    m_catMutex->Acquire();
    if (!m_database->Working()){
      m_catMutex->Release();
        return kError_DatabaseNotWorking;
    }
    char *data = m_database->Value(url);
    if (!data){
      m_catMutex->Release();
        return kError_ItemNotFound;
    }
    Error retvalue = kError_YouScrewedUp;
    if (*data == 'M')
        retvalue = RemoveSong(url);
    else if (*data == 'P')
        retvalue = RemovePlaylist(url);
    else if (*data == 'S')
        retvalue = RemoveStream(url);
    delete [] data;
    m_catMutex->Release();
    return retvalue;
}

Error MusicCatalog::Add(const char *url)
{
    assert(url);
    m_catMutex->Acquire();
    if (!m_database->Working()){
      m_catMutex->Release();
        return kError_DatabaseNotWorking;
    }
    char *data = m_database->Value(url);
    if (!data){
      m_catMutex->Release();
        return kError_ItemNotFound;
    }
    Error retvalue = kError_YouScrewedUp;
    if (*data == 'M')
        retvalue = AddSong(url);
    else if (*data == 'P')
        retvalue = AddPlaylist(url);
    else if (*data == 'S')
        retvalue = AddStream(url);
    delete [] data;
    m_catMutex->Release();
    return retvalue;
}

void MusicCatalog::ClearCatalog()
{
    m_catMutex->Acquire();

    vector<ArtistList *>::iterator a;
    vector<PlaylistItem *>::iterator p;

    for(a = m_artistList->begin(); a != m_artistList->end(); a++)
       delete (*a);
    delete m_artistList;

    for(p = m_unsorted->begin(); p != m_unsorted->end(); p++)
       delete (*p);
    delete m_unsorted;

    delete m_playlists;

    for(p = m_streams->begin(); p != m_streams->end(); p++)
       delete (*p);
    delete m_streams;

    m_trackCount = 0;

    m_artistList = new vector<ArtistList *>;
    m_unsorted = new vector<PlaylistItem *>;
    m_playlists = new vector<string>;
    m_streams = new vector<PlaylistItem *>;
    m_catMutex->Release();
}

Error MusicCatalog::RePopulateFromDatabase()
{
    if (!m_database->Working())
        return kError_DatabaseNotWorking;

    ClearCatalog();
    m_context->target->AcceptEvent(new Event(INFO_MusicCatalogCleared));

    int subversion = m_database->GetSubVersion();
    if (subversion < DATABASE_SUB_VERSION)
    {
        m_killGUIDs = true;
    }

    m_catMutex->Acquire(); 
    m_bSurpressAddMessages = true;
    char *key = m_database->NextKey(NULL);
    Error err = kError_NoErr;

    while (key) {
        err = Add(key);

        if (IsError(err)) { 
             //m_context->target->AcceptEvent(new ErrorMessageEvent("There was an internal error during generation of the Music Catalog"));
             //m_catMutex->Release();
             //return kError_YouScrewedUp;
        }
            
        key = m_database->NextKey(key);
    }
 
    if (subversion < DATABASE_SUB_VERSION)
        m_killGUIDs = false;
    m_database->StoreSubVersion(DATABASE_SUB_VERSION);

    m_catMutex->Release();
    m_bSurpressAddMessages = false;
    return kError_NoErr;
}

void MusicCatalog::SetDatabase(const std::string& path)
{
    if (m_database)
        delete m_database;

    m_database = new Database(path.c_str(), METADATABASE_VERSION);

    if (!m_database->Working()) {
        delete m_database;
        m_database = NULL;
    }

    if (m_database) {
        //if (m_database->IsUpgraded())
        //    m_context->target->AcceptEvent(new Event(INFO_DatabaseUpgraded));
        RePopulateFromDatabase();
        if (m_timeout == 0)
            PruneDatabase(true, true);
        Sort();
    }
}

typedef struct PruneThreadStruct {
    MusicCatalog *mc;
    Thread *thread;
    bool sendmessages;
} PruneThreadStruct;

void MusicCatalog::PruneDatabase(bool sendmessages, bool spawn)
{
    if (spawn) {
        Thread *thread = Thread::CreateThread();

        if (thread) {
            PruneThreadStruct *pts = new PruneThreadStruct;

            pts->mc = this;
            pts->sendmessages = sendmessages;
            pts->thread = thread;

            thread->Create(prune_thread_function, pts, true);
        }
    }
    else {
        PruneThread(sendmessages);
    }
}

void MusicCatalog::prune_thread_function(void *arg)
{
    PruneThreadStruct *pts = (PruneThreadStruct *)arg;

    pts->mc->PruneThread(pts->sendmessages);

    delete pts->thread;
    delete pts;
}

void MusicCatalog::PruneThread(bool sendmessages)
{
    m_catMutex->Acquire();
    char *key = m_database->NextKey(NULL);
    struct stat st;

    while (key) {
        if (!strncmp("file://", key, 7)) {
          string filename;

            if (IsntError(URLToFilePath(key, filename))) {
                if (-1 == stat(filename.c_str(), &st)) {
                    if (sendmessages) {
                        Remove(key);
                        key = NULL;
                    }
                    else {
                        m_database->Remove(key);
                        m_trackCount--;
                        key = NULL;
                    }
                }
            }
        }
        key = m_database->NextKey(key);
    }
    m_catMutex->Release();
}

void MusicCatalog::PruneDirectory(string &directory)
{
    m_catMutex->Acquire();

    char *key = m_database->NextKey(NULL);
    struct stat st;
 
    while (key) {
        if (!strncmp("file://", key, 7)) {
          string filename;
            
            if (IsntError(URLToFilePath(key, filename))) {
            if(!directory.compare(filename.c_str())) {
                    if (-1 == stat(filename.c_str(), &st)) {
                        Remove(key);
                        key = NULL;
                    }
                }
            }
        }
        key = m_database->NextKey(key);
    }
    m_catMutex->Release();
}

typedef struct MusicSearchThreadStruct {
    MusicCatalog *mc;
    vector<string> pathList;
    bool bSendMessages;
    Thread *thread;
} MusicSearchThreadStruct;

void MusicCatalog::SearchMusic(vector<string> &pathList, bool bBrowserMessages)
{
    if (!m_database->Working())
        return;

    Thread *thread = Thread::CreateThread();

    if (thread) {
        MusicSearchThreadStruct *mst = new MusicSearchThreadStruct;

        mst->mc = this;
        mst->pathList = pathList;
        mst->thread = thread;
        mst->bSendMessages = bBrowserMessages;

        thread->Create(musicsearch_thread_function, mst, true);
    }
}

void MusicCatalog::StopSearchMusic(void)
{
    m_exit = true;
}

void MusicCatalog::musicsearch_thread_function(void *arg)
{
    MusicSearchThreadStruct *mst = (MusicSearchThreadStruct *)arg;

    mst->mc->m_mutex->Acquire();

    mst->mc->m_exit = false;
    mst->mc->DoSearchPaths(mst->pathList, mst->bSendMessages);

    if (mst->bSendMessages)
        mst->mc->AcceptEvent(new Event(INFO_SearchMusicDone));

    mst->mc->m_mutex->Release();

    delete mst->thread;
    delete mst;
}

void MusicCatalog::DoSearchPaths(vector<string> &pathList, bool bSendMessages)
{
    vector<string>::iterator i;
    
    if (!bSendMessages)
        m_addImmediately = true;

    for(i = pathList.begin(); i != pathList.end(); i++)
        DoSearchMusic((char *)(*i).c_str(), bSendMessages);

    m_addImmediately = false;
}

void MusicCatalog::DoSearchMusic(char *path, bool bSendMessages)
{
    WIN32_FIND_DATA find;
    HANDLE handle;
    string search = path;

#ifndef WIN32
    if (!strcmp(path, "/dev") || !strcmp(path, "/proc"))
        return;
#endif
 
    if (bSendMessages) 
    {
        string info = string(_("Searching: ")) + search;
        m_context->player->AcceptEvent(new BrowserMessageEvent(info));
    }
   
    if (search[search.size() - 1] != DIR_MARKER)
       search.append(DIR_MARKER_STR);

    search.append("*");
#ifdef WIN32
    search.append(".*");
#endif

    handle = FindFirstFile((char *)search.c_str(), &find);

    if (handle != INVALID_HANDLE_VALUE)
    {
        do
        {
            char *fileExt;
#ifndef WIN32
            if (find.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK)
                continue;
#endif
            if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                if (!(!strcmp("." , find.cFileName) || !strcmp("..", find.cFileName)))
                {
                    string newDir = path; 
                    if (path[strlen(path) - 1] != DIR_MARKER)
                        newDir.append(DIR_MARKER_STR);
                    newDir.append(find.cFileName);

                    DoSearchMusic((char *)newDir.c_str(), bSendMessages);
                }
            }
            else 
            {
#ifdef WIN32
                string dirtest = path;
                if (path[strlen(path) - 1] != DIR_MARKER)
                    dirtest.append(DIR_MARKER_STR);
                dirtest.append(find.cFileName);

                struct stat stdir;

                stat(dirtest.c_str(), &stdir);
                if (stdir.st_mode & _S_IFDIR) {
                    DoSearchMusic((char *)dirtest.c_str(), bSendMessages);
                    continue;
                }
#endif          
                fileExt = m_context->player->GetExtension(find.cFileName);
                if (fileExt && m_context->plm->IsSupportedPlaylistFormat(fileExt) && 
                    strcmp("currentlist.m3u", find.cFileName))
                {
                    string file = path;
                string url;
                    file.append(DIR_MARKER_STR);
                    file.append(find.cFileName);

                    if (IsntError(FilePathToURL(file.c_str(), url)))
                        m_database->Insert(url.c_str(), "P");

                    if (m_addImmediately)
                        AddPlaylist(url.c_str());
                }
                else if (fileExt && 
                         m_context->player->IsSupportedExtension(fileExt)) {
                    string file = path;
                string tempurl;
                    file.append(DIR_MARKER_STR);
                    file.append(find.cFileName);

                    if (IsError(FilePathToURL(file.c_str(), tempurl))) {
                  tempurl = "";
                        delete [] fileExt;
                        continue;
                    }
                   
                    if (!bSendMessages) {
                        MetaData *tdata = ReadMetaDataFromDatabase(tempurl.c_str());
                        if (tdata) {
                            delete tdata;
                      tempurl = "";
                            delete [] fileExt;

                            continue;
                        }
                    }

                    PlaylistItem *plist = new PlaylistItem(tempurl.c_str());
                    m_context->plm->RetrieveMetaDataNow(plist);

                    MetaData *tempdata = ReadMetaDataFromDatabase(tempurl.c_str());

                    if (!tempdata|| !m_addImmediately) {
                        MetaData newmdata = (MetaData)plist->GetMetaData();
                        if (tempdata)
                            newmdata.SetGUID(tempdata->GUID());

                        WriteMetaDataToDatabase(tempurl, newmdata);
                    }
                    if (m_addImmediately){
                        fprintf(stderr, "adding %s\n", tempurl.c_str());
                        AddSong(tempurl.c_str());
                    }

                    if (tempdata)
                        delete tempdata;
                    delete plist;
                tempurl = "";
                }
                if (fileExt)
                    delete [] fileExt;
            }
        }
        while (FindNextFile(handle, &find) && !m_exit);
        FindClose(handle);
    }
}


/** Warning: this function has potential to produce buffer overflows.
 *  Has to be improved.
 */
void MusicCatalog::WriteMetaDataToDatabase(const std::string& url, 
                                           const MetaData metadata,
                                           MetadataStorageType type)
{
    if (!m_database->Working())
        return;

    string ost;
    char *num = new char[4096];
    char *temp = new char[1024];
    const char *kDatabaseDelimiter = " ";

    if (type == kTypeStream)
        ost.append("S");
    else
        ost.append("M");
    ost.append(kDatabaseDelimiter);
    ost.append("11");
    ost.append(kDatabaseDelimiter);

    sprintf(num, "%ld%s", (long int)metadata.Artist().size(), kDatabaseDelimiter);
    ost.append(num);
    sprintf(num, "%ld%s", (long int)metadata.Album().size(), kDatabaseDelimiter);
    ost.append(num);
    sprintf(num, "%ld%s", (long int)metadata.Title().size(), kDatabaseDelimiter);
    ost.append(num);
    sprintf(num, "%ld%s", (long int)metadata.Comment().size(), kDatabaseDelimiter);
    ost.append(num);
    sprintf(num, "%ld%s", (long int)metadata.Genre().size(), kDatabaseDelimiter);
    ost.append(num);
    sprintf(temp, "%ld", (long int)metadata.Year());
    sprintf(num, "%ld%s", (long int)strlen(temp), kDatabaseDelimiter);
    ost.append(num);
    sprintf(temp, "%ld", (long int)metadata.Track());
    sprintf(num, "%ld%s", (long int)strlen(temp), kDatabaseDelimiter);
    ost.append(num);
    sprintf(temp, "%ld", (long int)metadata.Time());
    sprintf(num, "%ld%s", (long int)strlen(temp), kDatabaseDelimiter);
    ost.append(num);
    sprintf(temp, "%ld", (long int)metadata.Size());
    sprintf(num, "%ld%s", (long int)strlen(temp), kDatabaseDelimiter);
    ost.append(num);
    sprintf(temp, "%ld", (long int)metadata.PlayCount());
    sprintf(num, "%ld%s", (long int)strlen(temp), kDatabaseDelimiter);
    ost.append(num);
    sprintf(num, "%ld%s", (long int)metadata.GUID().size(), kDatabaseDelimiter);
    ost.append(num);

    ost.append(metadata.Artist());
    ost.append(metadata.Album());
    ost.append(metadata.Title());
    ost.append(metadata.Comment());
    ost.append(metadata.Genre());
    sprintf(temp, "%ld", (long int)metadata.Year());
    ost.append(temp);
    sprintf(temp, "%ld", (long int)metadata.Track());
    ost.append(temp);
    sprintf(temp, "%ld", (long int)metadata.Time());
    ost.append(temp);
    sprintf(temp, "%ld", (long int)metadata.Size());
    ost.append(temp);
    sprintf(temp, "%ld", (long int)metadata.PlayCount());
    ost.append(temp);
    ost.append(metadata.GUID());
    ost.append("\0");

    m_database->Insert(url.c_str(), ost.c_str());

    delete [] num;
    delete [] temp;
}

MetaData *MusicCatalog::ReadMetaDataFromDatabase(const char *url)
{
    if (!m_database->Working())
        return NULL;

    char *dbasedata = m_database->Value(url);

    if (!dbasedata)
        return NULL;

    MetaData *metadata = new MetaData();
    char *value = dbasedata + 2;

    uint32_t numFields = 0;
    int offset = 0;

    sscanf(value, "%lu%n", (long unsigned int *)&numFields, &offset);
    uint32_t* fieldLength =  new uint32_t[numFields];

    int temp;

    for(uint32_t i = 0; i < numFields; i++)
    {
        sscanf(value + offset, " %lu %n", (long unsigned int *)&fieldLength[i],
                                        &temp);

        if (i == numFields - 1) {
            char intholder[10];
            sprintf(intholder, "%lu", (long unsigned int)fieldLength[i]);
            offset += strlen(intholder) + 1;
        }
        else
            offset += temp;
    }

    string data = value;
    data.erase(0, offset);

    uint32_t count = 0;
    string substring;

    for(uint32_t j = 0; j < numFields; j++)
    {
        if (fieldLength[j] == 0) 
            continue;

        substring = data.substr(count, fieldLength[j]);

        switch(j)
        {
            case 0:
                metadata->SetArtist(substring);
                break;
            case 1:
                metadata->SetAlbum(substring);
                break;
            case 2:
                metadata->SetTitle(substring);
                break;
            case 3:
                metadata->SetComment(substring);
                break;
            case 4:
                metadata->SetGenre(substring);
                break;
            case 5:
                metadata->SetYear(atoi(substring.c_str()));
                break;
            case 6:
                metadata->SetTrack(atoi(substring.c_str()));
                break;
            case 7:
                metadata->SetTime(atoi(substring.c_str()));
                break;
            case 8:
                metadata->SetSize(atoi(substring.c_str()));
                break;
            case 9:
                metadata->SetPlayCount(atoi(substring.c_str()));
                break;
            case 10: 
                metadata->SetGUID(substring);
                break;
            default:
                break;

        }

        count += fieldLength[j];
    }

    delete [] fieldLength;
    delete [] dbasedata;

    return metadata;
}

Error MusicCatalog::AcceptEvent(Event *e)
{
    switch (e->Type()) {
        case INFO_MusicCatalogTrackRemoved: {
            if (m_inUpdateSong) {
                MusicCatalogTrackRemovedEvent *mctr = 
                                             (MusicCatalogTrackRemovedEvent *)e;
                m_oldItem = (PlaylistItem *)mctr->Item();
                m_oldArtist = (ArtistList *)mctr->Artist();
                m_oldAlbum = (AlbumList *)mctr->Album();
     
                delete e;
            }
            else 
                m_context->target->AcceptEvent(e);
            break; }
        case INFO_MusicCatalogTrackAdded: {
            if (m_inUpdateSong) {
                MusicCatalogTrackAddedEvent *mcta =
                                             (MusicCatalogTrackAddedEvent *)e;
                m_newItem = (PlaylistItem *)mcta->Item();
                m_newArtist = (ArtistList *)mcta->Artist();
                m_newAlbum = (AlbumList *)mcta->Album();

                delete e;
            }
            else if (!m_bSurpressAddMessages)
                m_context->target->AcceptEvent(e);
            else
                delete e;
            break; }
        case INFO_SearchMusicDone: {
            m_context->target->AcceptEvent(new Event(INFO_MusicCatalogRegenerating));
            m_database->Sync();
            string info = _("Pruning the Music Catalog Database...");
            m_context->target->AcceptEvent(new BrowserMessageEvent(info));
            PruneDatabase();
            info = _("Regenerating the Music Catalog Database...");
            m_context->target->AcceptEvent(new BrowserMessageEvent(info));
            RePopulateFromDatabase();
            info = _("Sorting the Music Catalog Database...");
            m_context->target->AcceptEvent(new BrowserMessageEvent(info));
            Sort();
            m_context->target->AcceptEvent(new Event(INFO_SearchMusicDone));
            m_context->target->AcceptEvent(new Event(INFO_MusicCatalogDoneRegenerating));
            delete e;
            break;
        }
        case INFO_PrefsChanged: {
            int32_t watchtimeout = 0;
            m_context->prefs->GetPrefInt32(kWatchThisDirTimeoutPref, &watchtimeout);
            if (m_timeout != watchtimeout) {
                m_timeout = watchtimeout;
                if (m_timeout != 0) 
                    m_context->timerManager->SetTimer(m_watchTimer, m_timeout);
                else 
                    m_context->timerManager->StopTimer(m_watchTimer);
            }
            break;
        }
    }
    return kError_NoErr; 
}

void MusicCatalog::watch_timer(void *arg) 
{
    MusicCatalog *cat = (MusicCatalog*)arg;
    cat->WatchTimer();
}

void MusicCatalog::WatchTimer(void)
{
    bool welcome;
    m_context->prefs->GetPrefBoolean(kWelcomePref, &welcome);
    if (welcome)
        return;
/*
 #ifndef DEBUG_MUTEXES
    if (!m_timerMutex->Acquire()) {
 #else
    if (!m_timerMutex->__Acquire(__FILE__, __LINE__, 0)) {
 #endif
        return;
    }
*/
    m_timerMutex->Acquire();
    string watchDir;
    m_context->prefs->GetPrefString(kWatchThisDirectoryPref, &watchDir);

    vector<string> searchPaths;
    char *watchDir2 = new char[watchDir.length()];
    strcpy(watchDir2,watchDir.c_str());
    char *temp = strtok(watchDir2, ";");
    do {
        string dir = temp;
        searchPaths.push_back(dir);
        PruneDirectory(dir);
    } while ((temp = strtok(NULL, ";")));

    SearchMusic(searchPaths, false);

    delete [] watchDir2;

    m_timerMutex->Release();
}

/* arch-tag: f2dbcd07-a20b-4706-8cd0-c4df65b89654
   (do not change this comment) */



Generated by  Doxygen 1.6.0   Back to index