#include "GracenoteApi.h" #include "api.h" #include "../winamp/api_decodefile.h" #include #include #include #include GracenoteApi::GracenoteApi() { cddbInitialized = false; playlistInitialized = false; pCDDBControl=0; } GracenoteApi::~GracenoteApi() { } void GracenoteApi::Close() { if (pCDDBControl) { pCDDBControl->Shutdown(); pCDDBControl->Release(); pCDDBControl=0; } } static void SetProxy(const wchar_t *proxy, ICddbOptions *options) { wchar_t user[1024]=L""; wchar_t pass[1024]=L""; wchar_t server[1024]=L""; int port=80; if (!_wcsnicmp(proxy,L"https:",6)) port = 443; const wchar_t *skip = wcsstr(proxy, L"://"); if (skip) proxy = skip+3; skip = wcsstr(proxy, L"@"); if (skip) { const wchar_t *delimiter = wcsstr(proxy, L":"); if (delimiter < skip) // make sure there's really a password (and we didn't end up finding the port number) { StringCchCopyNW(user, 1024, proxy, delimiter-proxy); StringCchCopyNW(pass, 1024, delimiter+1, skip-(delimiter+1)); proxy=skip+1; } else { StringCchCopyNW(user, 1024, proxy, skip-proxy); proxy=skip+1; } } skip = wcsstr(proxy, L":"); // look for port if (skip) { StringCchCopyNW(server, 1024, proxy, skip-proxy); port = _wtoi(skip+1); } else StringCchCopyW(server, 1024, proxy); if (server[0]) options->put_ProxyServer(server); if (port) options->put_ProxyServerPort(port); if (user[0]) options->put_ProxyUserName(user); if (pass[0]) options->put_ProxyPassword(pass); } // {C0A565DC-0CFE-405a-A27C-468B0C8A3A5C} static const GUID internetConfigGroupGUID = { 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c } }; ICDDBControl2 *GracenoteApi::GetCDDB() { Nullsoft::Utility::AutoLock lock(cddbGuard); if (!cddbInitialized) { CoCreateInstance(__uuidof(CDDBNSWinampControl), 0, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pCDDBControl); if (pCDDBControl == NULL) return 0; #if 1 // TODO: benski> put back in once we can match winamp lang pack to a gracenote language ID // translate if necessary if (WASABI_API_LNG) { const wchar_t *langFolder = WASABI_API_LNG->GetLanguageFolder(); if (langFolder) { WIN32_FIND_DATAW find; wchar_t mask[MAX_PATH] = {0}, maskLoc[16] = {0}; // attempt to get the language code to help better guess the CDDB dll needed if(WASABI_API_LNG->GetLanguageIdentifier(LANG_IDENT_STR)) StringCchPrintfW(maskLoc, 16, L"CddbLang%s.dll", WASABI_API_LNG->GetLanguageIdentifier(LANG_LANG_CODE)); PathCombineW(mask, langFolder, maskLoc); HANDLE hFind = FindFirstFileW(mask, &find); // if the the guess provides nothing then scan for any valid dll (not ideal) if (hFind == INVALID_HANDLE_VALUE) { PathCombineW(mask, langFolder, L"CddbLang*.dll"); hFind = FindFirstFileW(mask, &find); } if (hFind != INVALID_HANDLE_VALUE) { ICddbUIPtr ui; ui.CreateInstance(__uuidof(CddbNSWinampUI)); if (ui) { long val = 0; wchar_t cddb_lang_fn[MAX_PATH] = {0}; PathCombineW(cddb_lang_fn, langFolder, find.cFileName); // TODO: benski> gracenote wants a "language ID" but we don't have a good way of knowing it :( ui->SetUILanguage(0, cddb_lang_fn, &val); // TODO: benski> also need to set ICddbOptions language ID } } FindClose(hFind); } } #endif // winamp browser id //HRESULT hr = pCDDBControl->SetClientInfo(L"7944448", L"F8DE207FBA826F136FF2C7EFE0AAB181", L"1", L"regstring"); //wa5's id const wchar_t *appVersion = WASABI_API_APP->main_getVersionNumString(); /* development client ID */ //HRESULT hr = pCDDBControl->SetClientInfo(L"3714048", L"B49286AE14F73CCD73C23B371A56DB00", const_cast(appVersion), L"regstring"); /* Beta Client ID */ //HRESULT hr = pCDDBControl->SetClientInfo(L"8337664", L"A222F2FA8B3E047291DFDBF465FD3C95", const_cast(appVersion), L"regstring"); /* Production Client ID */ BSTR appVersionBSTR = SysAllocString(appVersion); HRESULT hr = pCDDBControl->SetClientInfo(L"4896768", L"C1519CAE91489E405BCA93531837F2BE", appVersionBSTR, L"regstring"); SysFreeString(appVersionBSTR); if (FAILED(hr)) return 0; long flags = CACHE_UPDATE_FUZZY | CACHE_DONT_WRITE_ANY | CACHE_NO_LOOKUP_MEDIA; //CACHE_SUBMIT_ALL | CACHE_SUBMIT_OFFLINE | CACHE_SUBMIT_NEW | CACHE_NO_LOOKUP_MEDIA; // set cache path for cddb control ICddbOptionsPtr pOptions; hr = pCDDBControl->GetOptions(&pOptions); if (SUCCEEDED(hr)) { wchar_t dataPath[MAX_PATH] = {0}; PathCombineW(dataPath, WASABI_API_APP->path_getUserSettingsPath(), L"Plugins"); CreateDirectoryW(dataPath, 0); PathAppendW(dataPath, L"Gracenote"); CreateDirectoryW(dataPath, 0); hr = pOptions->put_LocalCachePath(dataPath); // initial cache flags //BOOL bOnline = SendMessage(line.hMainWindow, WM_USER, 0, 242); //if (!bOnline) //flags |= CACHE_DONT_CONNECT; // other needed settings hr = pOptions->put_ProgressEvents(FALSE); //hr = pOptions->put_LocalCacheFlags(CACHE_DONT_CREATE | CACHE_UPDATE_FUZZY | CACHE_SUBMIT_ALL); hr = pOptions->put_LocalCacheFlags(flags); hr = pOptions->put_LocalCacheSize(128 * 1024); // 128 megabyte limit on local cache size hr = pOptions->put_LocalCacheTimeout(5 * 365); // 5 years (e.g. when Gracenote contract runs out) hr = pOptions->put_TestSubmitMode(FALSE); //hr = pOptions->put_TestSubmitMode(TRUE); //CT> for BETA cycle... // this is supposed to turn off the spinning logo in the upper-left-hand corner hr = pOptions->put_ResourceModule(-1); // get n set proxy settings const wchar_t *proxy = AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", L""); if (proxy && proxy[0]) SetProxy(proxy, pOptions); // save settings hr = pCDDBControl->SetOptions(pOptions); } hr = pCDDBControl->Initialize(0/*(long)line.hMainWindow*/, (CDDBCacheFlags)flags); // checks for user registration long pVal=0; // this must be called when control is first initialized // this will load existing registration into control hr = pCDDBControl->IsRegistered(FALSE, &pVal); // if not reg'd, bring up reg UI (param1 = TRUE) if (!pVal) { // do headless registration ICddbUserInfoPtr pUser; hr = pCDDBControl->GetUserInfo(&pUser); if (pUser != NULL) { do { wchar_t strdata[129] = {0}; size_t size = sizeof(strdata)/sizeof(*strdata); wchar_t *str = strdata; GUID uid = GUID_NULL; int x; unsigned char *p; CoCreateGuid(&uid); p = (unsigned char *) & uid; //lstrcpynW(str, L"WA2_", 129); StringCchCopyExW(str, size, L"WA5_", &str, &size, 0); for (x = 0; x < sizeof(uid); x ++) { StringCchPrintfExW(str, size, &str, &size, 0, L"%02X", p[x]); //wsprintfW(str + wcslen(str), L"%02X", p[x]); } // user name will have to be unique per install hr = pUser->put_UserHandle(strdata); hr = pUser->put_Password(strdata); hr = pCDDBControl->SetUserInfo(pUser); } while (hr == CDDBSVCHandleUsed); } // this is just to check again that the user is now registered hr = pCDDBControl->IsRegistered(FALSE, &pVal); } cddbInitialized = true; } if (pCDDBControl) pCDDBControl->AddRef(); return pCDDBControl; } #if 0 /// This is the deprecated version of GetPlaylistManager that is no longer used without the MLDB manager ICddbPlaylist25Mgr *GracenoteApi::GetPlaylistManager() { ICddbPlaylist25Mgr *playlistMgr; ICDDBControl2 *cddb = GetCDDB(); if (!cddb) return 0; CoCreateInstance(__uuidof(CddbNSWinampPlaylist2Mgr), 0, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&playlistMgr); if (playlistMgr) { playlistMgr->AddRef(); wchar_t dataPath[MAX_PATH] = {0}; PathCombineW(dataPath, WASABI_API_APP->path_getUserSettingsPath(), L"Plugins"); CreateDirectoryW(dataPath, 0); PathAppendW(dataPath, L"Gracenote"); CreateDirectoryW(dataPath, 0); if (SUCCEEDED(playlistMgr->Initialize(cddb, dataPath))) { playlistMgr->DownloadCorrelates(0); playlistInitialized = true; } else { playlistMgr->Release(); playlistMgr=0; } } cddb->Release(); return playlistMgr; } #endif /// Caller is responsible for freeing the returned BSTR !!! BSTR SetAndCreatePath(const wchar_t *node) { wchar_t path_to_create[MAX_PATH] = {0}; BSTR bPath = 0; PathCombineW(path_to_create, WASABI_API_APP->path_getUserSettingsPath(), L"Plugins"); CreateDirectoryW(path_to_create, 0); PathAppendW(path_to_create, node); CreateDirectoryW(path_to_create, 0); bPath = SysAllocString(path_to_create); // modified path as return value return bPath; } /// This has superceded the old GetPlaylistManager /// Returns both a playlist manager in 'playlistMgr' and an mldb manager in 'mldbMgr' //int GracenoteApi::GetPlaylistManagerWithMLDBManager(ICddbPlaylist25Mgr **playlistMgr, ICddbMLDBManager **mldbMgr) int GracenoteApi::GetPlaylistManager(ICddbPlaylist25Mgr **playlistMgr, ICddbMLDBManager **mldbMgr) { ICddbPlaylist25Mgr *playlistMgrCreated; ICddbMLDBManager *mldbMgrCreated; ICDDBControl2 *cddb = GetCDDB(); if (!cddb) return 0; // Create the mldb manager CoCreateInstance(__uuidof(CddbMLDBManager), 0, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&mldbMgrCreated); if (mldbMgrCreated) { //PL_MLDB_FLAGS_ZERO 0x00000000 Used for initialization. //PL_MLDB_CHECK_BASE 0x00000001 Causes CheckDB to verify the consistency of the MLDB data file. //PL_MLDB_CHECK_INDEX 0x00000002 Causes CheckDB to verify the consistency of the MLDB index file. //PL_MLDB_CHECK_DEEP 0x00000004 Causes CheckDB to perform a thorough check of the MLDB file(s). //PL_MLDB_CHECK_DEFAULT 0x00000007 Default operation of CheckDB. //PL_MLDB_CLEAR_INIT_FLAG 0x00000010 Causes ModifyInitFlag to remove the file indicating Playlist is initialized. //PL_MLDB_SET_INIT_FLAG 0x00000020 Causes ModifyInitFlag to create the file indicating Playlist is initialized. //PL_MLDB_BACKUP_BASE 0x00000100 Causes BackupDB to create a backup copy of the MLDB data file. //PL_MLDB_BACKUP_INDEX 0x00000200 Causes BackupDB to create a backup copy of the MLDB index file. //PL_MLDB_RESTORE_BASE 0x00000400 Causes RestoreDB to restore the MLDB data file from the backup copy. //PL_MLDB_RESTORE_INDEX 0x00000800 Causes RestoreDB to restore the MLDB index file from the backup copy. //PL_MLDB_DELETE_INDEX 0x00001000 Causes DeleteDBFiles to remove the MLDB index file. //PL_MLDB_DELETE_BASE 0x00002000 Causes DeleteDBFiles to remove the MLDB data file. //PL_MLDB_DELETE_BACKUPS 0x00004000 Causes DeleteDBFiles to remove the MLDBbackup files. //PL_MLDB_DELETE_OTHER 0x00008000 Causes DeleteDBFiles to remove the other (non-MLDB) files used by Playlist. //PL_MLDB_AUTO_REINDEX 0x00010000 When specified in SetOptions, will cause theindex file to be automatically rebuilt atinitialization if it is corrupt. //PL_MLDB_AUTO_BACKUP 0x00020000 When specified in SetOptions, will cause the MLDB files to be automatically backed up at shutdown (if they have been modified). PL_MLDB_AUTO_MANAGE_INIT_FLAG 0x00040000 When specified in SetOptions, will cause the “init file” to be managed automatically (created at initialization, deleted at shut down). //PL_MLDB_AUTO_CHECK_IF_INIT_SET 0x00080000 When specified in SetOptions, will cause the MLDB files to be check at initialization if the “init file” exists (meaning shut down wasn’t called). //PL_MLDB_AUTO_CHECK_AT_INIT 0x00100000 When specified in SetOptions, will cause the MLDB files to be checked always at initialization. //PL_MLDB_AUTO_DEFAULT 0x000C0000 The default automatic behavior if no flags are specified with SetOptions. //PL_MLDB_DEVICE_MLDB_42 0x01000000 Enable Gracenote Device SDK 4.2 compatibility for MLDB, list, and correlates files //long autoFlags = PL_MLDB_AUTO_DEFAULT; //long autoFlags = PL_MLDB_AUTO_REINDEX | PL_MLDB_AUTO_BACKUP | PL_MLDB_AUTO_MANAGE_INIT_FLAG | PL_MLDB_AUTO_CHECK_IF_INIT_SET | PL_MLDB_AUTO_CHECK_AT_INIT; long autoFlags = PL_MLDB_AUTO_REINDEX | PL_MLDB_AUTO_BACKUP | PL_MLDB_AUTO_MANAGE_INIT_FLAG | PL_MLDB_AUTO_CHECK_IF_INIT_SET; BSTR bDataPath = SetAndCreatePath(L"Gracenote"); BSTR bBackupPath = SetAndCreatePath(L"Gracenote/Backup"); mldbMgrCreated->AddRef(); mldbMgrCreated->SetOptions(autoFlags, bBackupPath); CoCreateInstance(__uuidof(CddbNSWinampPlaylist2Mgr), 0, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&playlistMgrCreated); if (playlistMgrCreated) { playlistMgrCreated->AddRef(); mldbMgrCreated->Attach(playlistMgrCreated); // Attach the MLDB manager to the playlistMgr if (SUCCEEDED(playlistMgrCreated->Initialize(cddb, bDataPath))) { playlistMgrCreated->DownloadCorrelates(0); playlistInitialized = true; } else { playlistMgrCreated->Release(); playlistMgrCreated=0; } } SysFreeString(bDataPath); SysFreeString(bBackupPath); } cddb->Release(); *mldbMgr = mldbMgrCreated; *playlistMgr = playlistMgrCreated; if (mldbMgr && playlistMgr) return NErr_Success; else return NErr_FailedCreate; } /// Dont really have to use this, get the MLDB manager when creating the playlist manager ICddbMLDBManager *GracenoteApi::GetMLDBManager() { ICddbMLDBManager *mldbMgr; ICDDBControl2 *cddb = GetCDDB(); if (!cddb) return 0; CoCreateInstance(__uuidof(CddbMLDBManager), 0, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&mldbMgr); if (mldbMgr) { mldbMgr->AddRef(); } cddb->Release(); return mldbMgr; } HRESULT GracenoteApi::CreateFingerprint(ICDDBMusicIDManager *musicID, api_decodefile *decodeApi, ICddbFileInfo *info, const wchar_t *filename, long *killswitch) { if (!musicID || !decodeApi) return E_FAIL; ICddbMusicIDFingerprinterPtr fingerprinter; musicID->CreateFingerprinter(NULL, &fingerprinter); AudioParameters parameters; parameters.bitsPerSample = 16; parameters.channels = 2; parameters.sampleRate = 44100; ifc_audiostream *decoder = decodeApi->OpenAudioBackground(filename, ¶meters); if (decoder) { HRESULT hr = fingerprinter->BeginAudioStream((long)parameters.sampleRate, (long)parameters.bitsPerSample, (long)parameters.channels); char data[65536] = {0}; size_t decodeSize; int decode_killswitch=0, decode_error; while (decodeSize = decoder->ReadAudio((void *)data, sizeof(data), &decode_killswitch, &decode_error)) { if (decodeSize > LONG_MAX) // I _really_ doubt this is going to happen, but just in case, since we cast down to a long break; if (*killswitch) break; hr = fingerprinter->WriteAudioData(data, (long)decodeSize); if (hr == CDDBMusicID_FPAcquired) break; } ICddbMusicIDFingerprintPtr fingerprint; fingerprinter->EndAudioStream(&fingerprint); decodeApi->CloseAudio(decoder); hr=info->put_Fingerprint(fingerprint); return S_OK; } return E_FAIL; } ICDDBMusicIDManager3 *GracenoteApi::GetMusicID() { ICDDBControl2 *cddb = GetCDDB(); if (!cddb) return 0; ICDDBMusicIDManager3 *musicID; CoCreateInstance(__uuidof(CDDBNSWinampMusicIDManager), 0, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&musicID); if (musicID) musicID->Initialize(cddb); cddb->Release(); return musicID; } #define CBCLASS GracenoteApi START_DISPATCH; CB(API_GRACENOTE_GETCDDB, GetCDDB) CB(API_GRACENOTE_GETMUSICID, GetMusicID) CB(API_GRACENOTE_GETPLAYLISTMGR, GetPlaylistManager) //CB(API_GRACENOTE_GETPLAYLISTMGRWITHMLDBMGR, GetPlaylistManagerWithMLDBManager) CB(API_GRACENOTE_GETMLDBMGR, GetMLDBManager) CB(API_GRACENOTE_CREATEFINGERPRINT, CreateFingerprint) END_DISPATCH; #undef CBCLASS