aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_plg/IDScanner.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_plg/IDScanner.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Library/ml_plg/IDScanner.cpp')
-rw-r--r--Src/Plugins/Library/ml_plg/IDScanner.cpp698
1 files changed, 698 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_plg/IDScanner.cpp b/Src/Plugins/Library/ml_plg/IDScanner.cpp
new file mode 100644
index 00000000..2345ab06
--- /dev/null
+++ b/Src/Plugins/Library/ml_plg/IDScanner.cpp
@@ -0,0 +1,698 @@
+#include "IDScanner.h"
+#include "main.h"
+#include "../winamp/wa_ipc.h"
+#include "api__ml_plg.h"
+#include "playlist.h"
+#include <assert.h>
+#include <atlbase.h>
+#include <strsafe.h> // include this last
+
+//#define DEBUG_CALLBACKS
+
+IDScanner::IDScanner() : systemCallbacks(0)
+{
+ musicID=0;
+ killswitch=0;
+ filesComplete=0;
+ filesTotal=0;
+ state=STATE_IDLE;
+ m_dwCookie=0;
+ syscb_registered=false;
+
+ // Create the stack that will hold our batched up files for step 4 processing
+ //process_items;
+}
+
+IDScanner::~IDScanner()
+{
+ // ToDo: Make sure we clean up the processing stack here if we need to do that
+ //Shutdown();
+}
+
+void IDScanner::Shutdown()
+{
+ if (musicID)
+ {
+ IConnectionPoint *icp = GetConnectionPoint(musicID, DIID__ICDDBMusicIDManagerEvents);
+ if (icp)
+ {
+ icp->Unadvise(m_dwCookie);
+ icp->Release();
+ }
+ musicID->Shutdown();
+ musicID->Release();
+ }
+ musicID=0;
+
+ // Deregister the system callbacks
+ WASABI_API_SYSCB->syscb_deregisterCallback(this);
+}
+
+static HRESULT FillTag(ICddbFileInfo *info, BSTR filename)
+{
+ ICddbID3TagPtr infotag = NULL;
+ infotag.CreateInstance(CLSID_CddbID3Tag);
+ ICddbFileTag2_5Ptr tag2_5 = NULL;
+ infotag->QueryInterface(&tag2_5);
+ itemRecordW *record = AGAVE_API_MLDB->GetFile(filename);
+ if (record && infotag && tag2_5)
+ {
+ wchar_t itemp[64] = {0};
+ if (record->artist)
+ infotag->put_LeadArtist(record->artist);
+
+ if (record->album)
+ infotag->put_Album(record->album);
+
+ if (record->title)
+ infotag->put_Title(record->title);
+
+ if (record->genre)
+ infotag->put_Genre(record->genre);
+
+ if (record->track > 0)
+ infotag->put_TrackPosition(_itow(record->track, itemp, 10));
+
+// TODO: if (record->tracks > 0)
+
+ if (record->year > 0)
+ infotag->put_Year(_itow(record->year, itemp, 10));
+
+ if (record->publisher)
+ infotag->put_Label(record->publisher);
+
+ /*
+ if (GetFileInfo(filename, L"ISRC", meta, 512) && meta[0])
+ infotag->put_ISRC(meta);
+ */
+
+ if (record->disc > 0)
+ infotag->put_PartOfSet(_itow(record->disc, itemp, 10));
+
+ if (record->albumartist)
+ tag2_5->put_DiscArtist(record->albumartist);
+
+ if (record->composer)
+ tag2_5->put_Composer(record->composer);
+
+ if (record->length > 0)
+ tag2_5->put_LengthMS(_itow(record->length*1000, itemp, 10));
+
+ if (record->bpm > 0)
+ infotag->put_BeatsPerMinute(_itow(record->bpm, itemp, 10));
+
+ /*
+ if (GetFileInfo(filename, L"conductor", meta, 512) && meta[0])
+ tag2_5->put_Conductor(meta);
+ */
+ AGAVE_API_MLDB->FreeRecord(record);
+ }
+
+ if (info) info->put_Tag(infotag);
+
+ return S_OK;
+}
+
+void IDScanner::CommitFileInfo(ICddbFileInfo *match)
+{
+ ICddbFileTagPtr tag;
+ match->get_Tag(&tag);
+
+ ICddbDisc2Ptr disc1, disc;
+ match->get_Disc(&disc1);
+
+ ICddbDisc2_5Ptr disc2_5;
+ ICddbTrackPtr track;
+ ICddbTrack2_5Ptr track2;
+ if (disc1)
+ {
+ musicID->GetFullDisc(disc1, &disc);
+ if (disc == 0)
+ disc=disc1;
+ disc->QueryInterface(&disc2_5);
+ disc->GetTrack(1, &track);
+ if (track)
+ track->QueryInterface(&track2);
+ }
+
+ CComBSTR file, tagID, extData;
+ match->get_Filename(&file);
+ tag->get_FileId(&tagID);
+ playlistMgr->FileSetTagID(file, tagID, CDDB_UPDATE_NONE);
+
+ ICddbFileTag2_5Ptr tag2;
+ tag->QueryInterface(&tag2);
+ playlistMgr->FileSetFieldVal(file, gnpl_crit_field_xdev1, L"0"); // mark as done!
+
+ if (tag2) // try tag first
+ tag2->get_ExtDataSerialized(&extData);
+
+ if (!extData && track2 != 0) // WMA files don't get their tag object's extended data set correctly, so fallback to track extended data
+ track2->get_ExtDataSerialized(&extData);
+
+ if (!extData && disc2_5 != 0) // finally, fall back to disc extended data
+ disc2_5->get_ExtDataSerialized(&extData);
+
+ playlistMgr->FileSetExtDataSerialized(file, extData, CDDB_UPDATE_NONE);
+
+ if (tagID)
+ AGAVE_API_MLDB->SetField(file, "GracenoteFileID", tagID);
+ if (extData)
+ AGAVE_API_MLDB->SetField(file, "GracenoteExtData", extData);
+
+ // TODO: if we don't have an artist & album, we might as well grab this out of the tag now
+
+ // TODO: make thread-safe and optional
+ /*
+ updateFileInfo(file, L"GracenoteFileID", tagID);
+ updateFileInfo(file, L"GracenoteExtData", extData);
+ WriteFileInfo(file);
+ */
+}
+
+STDMETHODIMP STDMETHODCALLTYPE IDScanner::QueryInterface(REFIID riid, PVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ else if (IsEqualIID(riid, __uuidof(_ICDDBMusicIDManagerEvents)))
+ *ppvObject = (_ICDDBMusicIDManagerEvents *)this;
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch *)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG STDMETHODCALLTYPE IDScanner::AddRef(void)
+{
+ return 1;
+}
+
+ULONG STDMETHODCALLTYPE IDScanner::Release(void)
+{
+ return 0;
+}
+
+HRESULT STDMETHODCALLTYPE IDScanner::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ switch (dispid)
+ {
+ case 1: // OnTrackIDStatusUpdate, params: CddbMusicIDStatus Status, BSTR filename, long* Abort
+ {
+ //long *abort = pdispparams->rgvarg[0].plVal;
+ // TODO: is this safe to put here? Or does this make us get partial results
+ }
+ break;
+ case 2: // OnAlbumIDStatusUpdate, params: CddbMusicIDStatus Status, BSTR filename, long current_file, long total_files, long* Abort
+ {
+ long *abort = pdispparams->rgvarg[0].plVal;
+ /*long total_files = pdispparams->rgvarg[1].lVal;
+ long current_file= pdispparams->rgvarg[2].lVal;*/
+ CddbMusicIDStatus status = (CddbMusicIDStatus)pdispparams->rgvarg[4].lVal;
+ BSTR filename = pdispparams->rgvarg[3].bstrVal;
+
+ // TODO: is this safe to put here? Or does this make us get partial results
+ if (killswitch)
+ *abort = 1;
+ }
+ break;
+ case 3: // OnTrackIDComplete, params: LONG match_code, ICddbFileInfo* pInfoIn, ICddbFileInfoList* pListOut
+ break;
+ case 4:
+ break;// OnAlbumIDComplete, params: LONG match_code, ICddbFileInfoList* pListIn, ICddbFileInfoLists* pListsOut
+ case 5:
+ break; // OnGetFingerprint
+ case 6:
+ break;
+ case 7://OnLibraryIDListStarted
+ break;
+ case 8: // OnLibraryIDListComplete
+ {
+
+ long *abort = pdispparams->rgvarg[0].plVal;
+ if (killswitch)
+ *abort = 1;
+ /*long FilesError =pdispparams->rgvarg[1].lVal;
+ long FilesNoMatch=pdispparams->rgvarg[2].lVal;
+ long FilesFuzzy=pdispparams->rgvarg[3].lVal;
+ long FilesExact=pdispparams->rgvarg[4].lVal;*/
+ filesTotal=pdispparams->rgvarg[5].lVal;
+ filesComplete=pdispparams->rgvarg[6].lVal;
+ IDispatch *disp = pdispparams->rgvarg[7].pdispVal;
+ if (disp)
+ {
+ ICddbFileInfoList* matchList=0;
+ disp->QueryInterface(&matchList);
+ if (matchList)
+ {
+ long matchcount;
+ matchList->get_Count(&matchcount);
+ for (int j = 1;j <= matchcount;j++)
+ {
+ ICddbFileInfoPtr match;
+ matchList->GetFileInfo(j, &match);
+ CommitFileInfo(match);
+ }
+
+ matchList->Release();
+ }
+ return S_OK;
+ }
+ else
+ return E_FAIL;
+
+ }
+ break;
+ case 9: //OnLibraryIDComplete
+ break;
+ case 10: // OnGetFingerprintInfo
+ {
+ long *abort = pdispparams->rgvarg[0].plVal;
+ IDispatch *disp = pdispparams->rgvarg[1].pdispVal;
+ BSTR filename = pdispparams->rgvarg[2].bstrVal;
+
+ ICddbFileInfoPtr info;
+ disp->QueryInterface(&info);
+ return AGAVE_API_GRACENOTE->CreateFingerprint(musicID, AGAVE_API_DECODE, info, filename, abort);
+ }
+ break;
+ case 11: // OnGetTagInfo
+ {
+ pdispparams->rgvarg[0].plVal;
+ IDispatch *disp = pdispparams->rgvarg[1].pdispVal;
+ BSTR filename = pdispparams->rgvarg[2].bstrVal;
+
+ ICddbFileInfoPtr info;
+ disp->QueryInterface(&info);
+ return FillTag(info, filename);
+ }
+ break;
+ }
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT STDMETHODCALLTYPE IDScanner::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ *rgdispid = DISPID_UNKNOWN;
+ return DISP_E_UNKNOWNNAME;
+}
+
+HRESULT STDMETHODCALLTYPE IDScanner::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE IDScanner::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ return E_NOTIMPL;
+}
+
+void IDScanner::SetGracenoteData(BSTR filename, BSTR tagID, BSTR extData)
+{
+ bool foundExt=false;
+ if (extData && extData[0])
+ {
+ playlistMgr->FileSetExtDataSerialized(filename, extData, CDDB_UPDATE_NONE);
+ CComBSTR test;
+ playlistMgr->FileGetExtDataSerialized(filename, &test, 0); // benski> 24 Jul 2007 - there is a currently a bug that makes this always E_FAIL
+ if (test)
+ foundExt=true;
+ }
+
+ if (!foundExt) // no Extended Data (or invalid), but we have a Tag ID, we'll ask the playlist SDK to do a quick lookup
+ {
+ playlistMgr->FileSetTagID(filename, tagID, CDDB_UPDATE_EXTENDED);
+
+ // write back to Media Library database
+ CComBSTR extData;
+ playlistMgr->FileGetExtDataSerialized(filename, &extData, 0); // benski> 24 Jul 2007 - there is a currently a bug that makes this always E_FAIL
+ if (extData)
+ AGAVE_API_MLDB->SetField(filename, "GracenoteExtData", extData);
+ }
+ else
+ playlistMgr->FileSetTagID(filename, tagID, CDDB_UPDATE_NONE);
+}
+
+/*
+//void IDScanner::ProcessDatabaseDifferences(Device * dev, C_ItemList * ml0,C_ItemList * itemRecordsOnDevice, C_ItemList * itemRecordsNotOnDevice, C_ItemList * songsInML, C_ItemList * songsNotInML)
+void IDScanner::ProcessDatabaseDifferences(Device * dev, C_ItemList * ml0,C_ItemList * itemRecordsOnDevice, C_ItemList * itemRecordsNotOnDevice, C_ItemList * songsInML, C_ItemList * songsNotInML)
+{
+ C_ItemList device2;
+ C_ItemList *device0=&device2;
+
+ int l = dev->getPlaylistLength(0);
+ for(int i=0; i<l; i++) device0->Add((void*)dev->getPlaylistTrack(0,i));
+
+
+ qsort(ml0->GetAll(),ml0->GetSize(),sizeof(void*),sortfunc_ItemRecords);
+ nu::qsort(device0->GetAll(), device0->GetSize(), sizeof(void*), dev, compareSongs);
+
+ C_ItemList *ml = new C_ItemList;
+ C_ItemList *device = new C_ItemList;
+
+ int i,j;
+
+ {
+ itemRecordW * lastice = NULL;
+ songid_t lastsong = NULL;
+ for(i=0; i<ml0->GetSize(); i++) {
+ itemRecordW * it = (itemRecordW*)ml0->Get(i);
+ if(lastice) if(compareItemRecords(lastice,it)==0) continue;
+ ml->Add(it);
+ lastice = it;
+ }
+ for(i=0; i<device0->GetSize(); i++) {
+ songid_t song = (songid_t)device0->Get(i);
+ if(lastsong) if(compareSongs((void*)&song,(void*)&lastsong, dev)==0) continue;
+ device->Add((void*)song);
+ lastsong = song;
+ }
+ }
+
+ i=0,j=0;
+ int li = device->GetSize();
+ int lj = ml->GetSize();
+ while(i<li && j<lj) {
+ itemRecordW * it = (itemRecordW*)ml->Get(j);
+ songid_t song = (songid_t)device->Get(i);
+
+ int cmp = compareItemRecordAndSongId(it,song, dev);
+ if(cmp == 0) { // song on both
+ if(itemRecordsOnDevice) itemRecordsOnDevice->Add(it);
+ if(songsInML) songsInML->Add((void*)song);
+ i++;
+ j++;
+ }
+ else if(cmp > 0) { //song in ml and not on device
+ if(itemRecordsNotOnDevice) itemRecordsNotOnDevice->Add(it);
+ j++;
+ }
+ else { // song on device but not in ML
+ if(songsNotInML) songsNotInML->Add((void*)song);
+ i++;
+ }
+ }
+ // any leftovers?
+ if(songsNotInML) while(i<li) {
+ songid_t song = (songid_t)device->Get(i++);
+
+ songsNotInML->Add((void*)song);
+ }
+ if(itemRecordsNotOnDevice) while(j<lj) {
+ itemRecordW * it = (itemRecordW *)ml->Get(j++);
+
+ itemRecordsNotOnDevice->Add(it);
+ }
+ delete ml; delete device;
+}
+*/
+
+/*
+2-pass strategy
+Pass 1: Find all tracks with Gracenote Extended Data
+Pass 2: Find File ID & extended data by fingerprint
+*/
+void IDScanner::ScanDatabase()
+{
+ filesComplete=0;
+ filesTotal=0;
+ state=STATE_INITIALIZING;
+ killswitch=0; // reset just in case
+
+ if (SetupPlaylistSDK())
+ {
+ // If this is our first time running then lets register the wasabi system callbacks for adding and removing tracks
+ if (!syscb_registered)
+ {
+ WASABI_API_SYSCB->syscb_registerCallback(this);
+ syscb_registered = true;
+ }
+
+ // Set up the MLDB manager
+ InitializeMLDBManager();
+
+ state=STATE_SYNC;
+ /* Get a list of files in the media library database */
+ itemRecordListW *results = AGAVE_API_MLDB->Query(L"type=0");
+ if (results)
+ {
+ filesTotal=results->Size;
+ for (int i=0;i<results->Size;i++)
+ {
+ if (killswitch)
+ break;
+ wchar_t * filename = results->Items[i].filename;
+ HRESULT hr=playlistMgr->AddEntry(filename); // Add entry to gracenote DB
+ assert(SUCCEEDED(S_OK));
+ if (hr == S_OK)
+ {
+ // Fill in Artist & Album info since we have it in the itemRecordList anyway
+ // TODO: probably want to use SKIP_THE_AND_WHITESPACE here
+ if (results->Items[i].album && results->Items[i].album[0])
+ hr=playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_album_name, results->Items[i].album);
+
+ if (results->Items[i].artist && results->Items[i].artist[0])
+ hr=playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_track_artist_name, results->Items[i].artist);
+
+ // Populate title information so that we have more complete data.
+ if (results->Items[i].title && results->Items[i].title[0])
+ hr=playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_track_name, results->Items[i].title);
+
+ wchar_t storage[64] = {0};
+ // Populate the file length in milliseconds
+ if (results->Items[i].length > 0)
+ {
+ _itow(results->Items[i].length,storage, 10);
+ hr=playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_file_length, storage);
+ }
+
+ // Populate the file size in kilobytes
+ if (results->Items[i].filesize > 0)
+ {
+ _itow(results->Items[i].filesize,storage, 10);
+ hr=playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_file_size, storage);
+ }
+
+ wchar_t *tagID = getRecordExtendedItem(&results->Items[i], L"GracenoteFileID");
+ if (tagID && tagID[0])
+ {
+ SetGracenoteData(filename, tagID, getRecordExtendedItem(&results->Items[i], L"GracenoteExtData"));
+ hr=playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_xdev1, L"0"); // done with this file!
+ }
+ else
+ hr=playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_xdev1, L"1"); // move to phase 1
+ }
+ filesComplete=i+1;
+ }
+
+ AGAVE_API_MLDB->FreeRecordList(results);
+ state=STATE_METADATA;
+ filesComplete=0;
+ if (!killswitch)
+ Pass1();
+ filesComplete=0;
+ state=STATE_MUSICID;
+ if (!killswitch)
+ Pass2();
+ state=STATE_DONE;
+ if (!killswitch)
+ AGAVE_API_MLDB->Sync();
+ }
+ // Set the pass 2 flag back so that on next generation we dont try to run it
+ run_pass2_flag = false;
+ }
+ else
+ state=STATE_ERROR;
+}
+
+bool IDScanner::GetStatus(long *pass, long *track, long *tracks)
+{
+ *pass = state;
+ *track = filesComplete;
+ *tracks = filesTotal;
+ return true;
+}
+
+// System callback handlers from WASABI
+
+FOURCC IDScanner::GetEventType()
+{
+ return api_mldb::SYSCALLBACK;
+}
+int IDScanner::notify(int msg, intptr_t param1, intptr_t param2)
+{
+ wchar_t *filename = (wchar_t *)param1;
+
+ switch (msg)
+ {
+ case api_mldb::MLDB_FILE_ADDED:
+ {
+
+ DebugCallbackMessage(param1, L"File Added: '%s'");
+
+ // Call the add/update function that needs to run on our lonesome playlist generator thread
+ WASABI_API_THREADPOOL->RunFunction(plg_thread, IDScanner::MLDBFileAddedOnThread, _wcsdup(filename), (intptr_t)this, api_threadpool::FLAG_REQUIRE_COM_STA);
+ }
+ break;
+ case api_mldb::MLDB_FILE_REMOVED_PRE:
+ {
+ // We are not concerned with the PRE scenario
+ //DebugCallbackMessage(param1, L"File Removed PRE: '%s'");
+ }
+ break;
+ case api_mldb::MLDB_FILE_REMOVED_POST:
+ {
+ WASABI_API_THREADPOOL->RunFunction(plg_thread, IDScanner::MLDBFileRemovedOnThread, _wcsdup(filename), (intptr_t)this, api_threadpool::FLAG_REQUIRE_COM_STA);
+ // We will only care about the post scenario since we just need to remove the file entry from gracenote.
+ //DebugCallbackMessage(param1, L"File Removed POST: '%s'");
+ }
+ break;
+ case api_mldb::MLDB_FILE_UPDATED:
+ {
+ // For now we call the add method even on an update
+ WASABI_API_THREADPOOL->RunFunction(plg_thread, IDScanner::MLDBFileAddedOnThread, _wcsdup(filename), (intptr_t)this, api_threadpool::FLAG_REQUIRE_COM_STA);
+ //DebugCallbackMessage(param1, L"File Updated: '%s'");
+ }
+ break;
+ case api_mldb::MLDB_CLEARED:
+ {
+ WASABI_API_THREADPOOL->RunFunction(plg_thread, IDScanner::MLDBClearedOnThread, 0, (intptr_t)this, api_threadpool::FLAG_REQUIRE_COM_STA);
+ //DebugCallbackMessage(param1, L"MLDB Cleared");
+ }
+ break;
+ default: return 0;
+ }
+ return 1;
+}
+
+// Outputs a messagebox with a filename to know when callbacks are being triggered
+inline void IDScanner::DebugCallbackMessage(const intptr_t text, const wchar_t *message)
+{
+//#ifdef DEBUG_CALLBACKS
+#if defined(DEBUG) && defined(DEBUG_CALLBACKS)
+ const int size = MAX_PATH + 256;
+ wchar_t *filename = (wchar_t *)text;
+ wchar_t buff[size] = {0};
+
+ //wsprintf(buff, size, message, filename);
+ StringCchPrintf(buff, size, message, filename);
+ MessageBox(0, buff, L"Wasabi Callback Debug", 0);
+#endif
+}
+
+int IDScanner::MLDBFileAddedOnThread(HANDLE handle, void *user_data, intptr_t id)
+{
+ if (!playlistMgr) return 0;
+
+ // Variables to hold information about the file query
+ wchar_t *filename = (wchar_t *)user_data;
+ IDScanner *scanner = (IDScanner *)id;
+
+ wchar_t buff[1024] = {0};
+ _ltow(scanner->state, buff, 10);
+
+ itemRecordW *result = AGAVE_API_MLDB->GetFile(filename);
+
+ HRESULT hr=playlistMgr->AddEntry(filename); // Add the file entry to the gracenote DB
+
+ assert(SUCCEEDED(S_OK));
+
+ if (hr == S_OK /*&& results->Size == 1*/)
+ {
+ // Fill in Artist & Album info since we have it in the itemRecordList anyway
+ // TODO: probably want to use SKIP_THE_AND_WHITESPACE here
+ if (result->album && result->album[0])
+ playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_album_name, result->album);
+
+ if (result->artist && result->artist[0])
+ playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_track_artist_name, result->artist);
+
+ // Populate title, file size, and length information so that we have more complete data.
+ if (result->title && result->title[0])
+ playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_track_name, result->title);
+
+
+ wchar_t storage[64] = {0};
+ // Populate the file length in milliseconds
+ if (result->length > 0)
+ {
+ _itow(result->length,storage, 10);
+ playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_file_length, storage);
+ }
+
+ // Populate the file size in kilobytes
+ if (result->filesize > 0)
+ {
+ _itow(result->filesize,storage, 10);
+ playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_file_size, storage);
+ }
+
+ wchar_t *tagID = getRecordExtendedItem(result, L"GracenoteFileID");
+ if (tagID && tagID[0])
+ {
+ scanner->SetGracenoteData(filename, tagID, getRecordExtendedItem(result, L"GracenoteExtData"));
+ // We have everything we need at this point in the gracenote DB
+ playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_xdev1, L"0"); // done with this file!
+ }
+ else // Set it to the final scan
+ {
+ playlistMgr->FileSetFieldVal(filename, gnpl_crit_field_xdev1, L"2"); // move to phase 2, we can skip phase 1
+ // Add the current file to the step 4 processing stack
+ // TODOD is there a mem leak here??
+ ProcessItem *itemz = new ProcessItem();
+ itemz->filename = filename;
+ //scanner->process_items.push(*itemz); // Add the current item coming in to the queue
+
+ // Set the flag so that we know we will need to rerun step 4 (pass 2) on a playlist regeneration, this only needs to happen if there is an actual change.
+ run_pass2_flag = true;
+ }
+ }
+
+ if (result)
+ AGAVE_API_MLDB->FreeRecord(result);
+
+ // ToDo: We need to do this free when we pop it off of the processing stack later
+ free(filename); // Clean up the user data
+ return 0;
+}
+
+int IDScanner::MLDBFileRemovedOnThread(HANDLE handle, void *user_data, intptr_t id)
+{
+ wchar_t *filename = (wchar_t *)user_data;
+
+ HRESULT hr = playlistMgr->DeleteFile(filename);
+
+ if (hr == S_OK)
+ return 0;
+ else
+ return 1;
+
+ free(filename); // Clean up the user data
+}
+
+int IDScanner::MLDBClearedOnThread(HANDLE handle, void *user_data, intptr_t id)
+{
+ return ResetDB(false);
+}
+
+int IDScanner::ProcessStackItems(void)
+{
+ // ToDo: Run through the stack items and process stage 4 on them
+ //this->
+ return 1;
+}
+
+#define CBCLASS IDScanner
+START_DISPATCH;
+CB(SYSCALLBACK_GETEVENTTYPE, GetEventType);
+CB(SYSCALLBACK_NOTIFY, notify);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file