From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- Src/Plugins/Library/ml_online/Main.cpp | 599 +++++++++++++++++++++++++++++++++ 1 file changed, 599 insertions(+) create mode 100644 Src/Plugins/Library/ml_online/Main.cpp (limited to 'Src/Plugins/Library/ml_online/Main.cpp') diff --git a/Src/Plugins/Library/ml_online/Main.cpp b/Src/Plugins/Library/ml_online/Main.cpp new file mode 100644 index 00000000..4f4f3c86 --- /dev/null +++ b/Src/Plugins/Library/ml_online/Main.cpp @@ -0,0 +1,599 @@ +#include "main.h" +#include "./api__ml_online.h" +#include "./config.h" +#include "./navigation.h" +#include "./resource.h" +#include "./preferences.h" +#include "./serviceHelper.h" + +#include "../../General/gen_ml/ml.h" +#include "../../General/gen_ml/ml_ipc_0313.h" + +#include "../nu/MediaLibraryInterface.h" +#include "../nu/AutoChar.h" +#include "../nu/ns_wc.h" +#include "../nu/AutoWide.h" +#include +#include "../nu/nonewthrow.c" +#include "../nu/ConfigCOM.h" + +#include "BufferCache.h" +#include "OMCOM.h" +#include "JNetCom.h" // for buffer_map + +#include +#include + + +static int Plugin_Init(); +static void Plugin_Quit(); +static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3); + +static Navigation *navigation = NULL; +static std::vector *unloadCallbacks = NULL; + +C_Config *g_config=NULL; + +extern "C" winampMediaLibraryPlugin plugin = +{ + MLHDR_VER, + "nullsoft(ml_online.dll)", + Plugin_Init, + Plugin_Quit, + Plugin_MessageProc, + 0, + 0, + 0, +}; + + +HINSTANCE Plugin_GetInstance(void) +{ + return plugin.hDllInstance; +} + +HWND Plugin_GetWinamp(void) +{ + return plugin.hwndWinampParent; +} + +HWND Plugin_GetLibrary(void) +{ + return plugin.hwndLibraryParent; +} + +HRESULT Plugin_GetNavigation(Navigation **instance) +{ + if(NULL == instance) return E_POINTER; + + if (NULL == navigation) + { + *instance = NULL; + return E_UNEXPECTED; + } + + *instance = navigation; + navigation->AddRef(); + + return S_OK; +} + +typedef struct __PLUGINTIMERREC +{ + UINT_PTR id; + PLUGINTIMERPROC callback; + ULONG_PTR data; +} PLUGINTIMERREC; + +typedef std::vector PluginTimerList; + +static void CALLBACK Plugin_TimerProcDispath(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elapsedMs) +{ + HWND hLibrary = Plugin_GetLibrary(); + if (NULL != hLibrary && FALSE != IsWindow(hLibrary)) + { + PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData"); + if (NULL != list) + { + size_t index = list->size(); + while(index--) + { + PLUGINTIMERREC *rec = &list->at(index); + if (rec->id == eventId) + { + rec->callback(eventId, elapsedMs, rec->data); + return; + } + } + } + } + + KillTimer(hwnd, eventId); +} + +UINT_PTR Plugin_SetTimer(UINT elapseMs, PLUGINTIMERPROC callback, ULONG_PTR data) +{ + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary || FALSE == IsWindow(hLibrary)) + return 0; + + if (GetCurrentThreadId() != GetWindowThreadProcessId(hLibrary, NULL)) + return 0; + + if (NULL == callback) + return 0; + + PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData"); + if (NULL == list) + { + list = new PluginTimerList(); + if (NULL == list) return 0; + if (0 == SetProp(hLibrary, L"OnlineMediaTimerData", list)) + { + delete(list); + return 0; + } + } + + PLUGINTIMERREC rec; + rec.data = data; + rec.callback = callback; + rec.id = SetTimer(NULL, NULL, elapseMs, Plugin_TimerProcDispath); + if (0 == rec.id) + { + if (0 == list->size()) + { + RemoveProp(hLibrary, L"OnlineMediaTimerData"); + delete(list); + } + return 0; + } + + list->push_back(rec); + return rec.id; +} + +void Plugin_KillTimer(UINT_PTR eventId) +{ + KillTimer(NULL, eventId); + + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary || FALSE == IsWindow(hLibrary)) + return; + + PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData"); + if (NULL == list) return; + + size_t index = list->size(); + while(index--) + { + if (list->at(index).id == eventId) + { + list->erase(list->begin() + index); + break; + } + } + + if (0 == list->size()) + { + RemoveProp(hLibrary, L"OnlineMediaTimerData"); + delete(list); + } +} + +static void Plugin_UninitializeTimer() +{ + HWND hLibrary = Plugin_GetLibrary(); + if (NULL == hLibrary || FALSE == IsWindow(hLibrary)) + return; + + PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData"); + RemoveProp(hLibrary, L"OnlineMediaTimerData"); + if (NULL == list) return; + + size_t index = list->size(); + while(index--) + { + KillTimer(NULL, list->at(index).id); + } + + delete(list); +} + + +wchar_t g_w_cachedir[2048] = {0}; +int winampVersion=0; + +OMCOM omCOM; + +URLMap urlMap; +MetadataMap metadataMap; +Nullsoft::Utility::LockGuard urlMapGuard; + +void LoadCacheItem( wchar_t *path ) +{ + FILECACHETYPE cachefile = {0}; + unsigned long size = 0; + HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) return; + ReadFile(hFile, &cachefile, sizeof(FILECACHETYPE),&size, NULL); + if ( size == sizeof(FILECACHETYPE )) + { + size = 0; + time_t now = time(NULL); + //read the header, validate + if ( cachefile.version == FILECACHEVERSION ) + { + if ( now < cachefile.expires ) + { + char *url = (char *)calloc((size_t)cachefile.urllen, sizeof(char)); + if (url) + { + size = 0; + ReadFile(hFile, url, (DWORD)cachefile.urllen, &size, NULL); + if ( cachefile.urllen == size ) // we read it ok! + { + char tempbuf[16384] = {0}; + Buffer_GrowBuf *newbuffer = new Buffer_GrowBuf; + INT64 readin=0; + newbuffer->expire_time = (time_t)cachefile.expires; + while ( readin != cachefile.datalen ) + { + DWORD toread=(DWORD)cachefile.datalen - (DWORD)readin; + if ( toread > 16384 ) toread=16384; + size = 0; + int success = ReadFile(hFile, &tempbuf, toread , &size, NULL); + if ( success ) + { + if ( size ) + { + newbuffer->add(tempbuf,(int)size); + readin += size; + } + } + else + { + break; + } + } + if ( readin != cachefile.datalen ) + { + delete newbuffer; + } + else + { + buffer_map[(wchar_t *)AutoWide(url)]=newbuffer; + } + } + else + { + free(url); + } + } + } + } + } + CloseHandle(hFile); + DeleteFile(path); +} + +void LoadCache() +{ + WIN32_FIND_DATA FindFileData = {0}; + HANDLE hFind; + wchar_t searchs[2048] = {0}; + + buffer_map.clear(); + + StringCchPrintf(searchs, 2048, L"%s\\*.w5x",g_w_cachedir); + hFind = FindFirstFile(searchs, &FindFileData); + if ( hFind != INVALID_HANDLE_VALUE ) + { + do + { + wchar_t activefile[2048] = {0}; + StringCchPrintf(activefile, 2048, L"%s\\%s",g_w_cachedir,FindFileData.cFileName); + LoadCacheItem(activefile); + } while ( FindNextFile(hFind, &FindFileData) ); + FindClose(hFind); + } +} + +void SaveCache() +{ + BufferMap::iterator buffer_it; + DWORD start=0xABBACAFE; + for(buffer_it = buffer_map.begin();buffer_it != buffer_map.end(); buffer_it++) + { + time_t now = time(NULL); + if ( buffer_it->second->expire_time > now ) + { + wchar_t filename[2048] = {0}; + FILECACHETYPE cachefile; + HANDLE hFile; + INT64 size=0; + memset((void *)&cachefile,0,sizeof(FILECACHETYPE)); + cachefile.version = FILECACHEVERSION; + cachefile.expires = buffer_it->second->expire_time; + AutoChar charUrl(buffer_it->first.c_str()); + cachefile.urllen = strlen(charUrl)+1; + cachefile.datalen = buffer_it->second->getlen()+1; + + StringCchPrintf(filename, 2048, L"%s\\%08X.w5x",g_w_cachedir,start++); + hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, &cachefile, sizeof(FILECACHETYPE),(LPDWORD)&size,NULL); + if ( size == sizeof(FILECACHETYPE) ) + { + char blank[2]="\0"; + size = 0; WriteFile(hFile, (char *)charUrl ,(DWORD)cachefile.urllen, (LPDWORD)&size, NULL); + size = 0; WriteFile(hFile, buffer_it->second->get() , (DWORD)buffer_it->second->getlen(), (LPDWORD)&size, NULL); + size = 0; WriteFile(hFile, blank , 1, (LPDWORD)&size, NULL); + } + else + { + CloseHandle(hFile); + hFile=NULL; + DeleteFile(filename); + } + } + if (hFile) + { + CloseHandle(hFile); + hFile=NULL; + } + } + } +} + +void initConfigCache() +{ + wchar_t iniFileName[2048] = {0}; + mediaLibrary.BuildPath(L"Plugins\\ml", iniFileName, 2048); + CreateDirectory(iniFileName, NULL); + mediaLibrary.BuildPath(L"Plugins\\ml\\cache", g_w_cachedir, 2048); + CreateDirectory(g_w_cachedir, NULL); + mediaLibrary.BuildPath(L"Plugins\\ml\\ml_online.ini", iniFileName, 2048); + AutoChar charFn(iniFileName); + g_config = new C_Config(AutoChar(iniFileName)); + + int x = g_config->ReadInt("maxbandwidth", MAXBANDWIDTH ); + g_config->WriteInt("maxbandwidth",x); + + x = g_config->ReadInt("minbandwidth",1); + g_config->WriteInt("minbandwidth",x); + + LoadCache(); +} + +static void Plugin_ExecuteOpenOnce() +{ + CHAR szBuffer[128] = {0}; + INT cchLen = Config_ReadStr("Navigation", "openOnce", NULL, szBuffer, ARRAYSIZE(szBuffer)); + if (0 != cchLen) + { + UINT serviceId; + if (FALSE != StrToIntExA(szBuffer, STIF_SUPPORT_HEX, (INT*)&serviceId)) + { + + cchLen = Config_ReadStr("Navigation", "openOnceMode", NULL, szBuffer, ARRAYSIZE(szBuffer)); + UINT showMode; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "popup", -1, szBuffer, cchLen)) + showMode = SHOWMODE_POPUP; + else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "ensureVisible", -1, szBuffer, cchLen)) + showMode = SHOWMODE_ENSUREVISIBLE; + else + showMode = SHOWMODE_NORMAL; + + ServiceHelper_ShowService(serviceId, showMode); + } + + Config_WriteStr("Navigation", "openOnce", NULL); + Config_WriteStr("Navigation", "openOnceMode", NULL); + } +} + +static int Plugin_Init() +{ + if (FAILED(WasabiApi_Initialize(plugin.hDllInstance, plugin.service))) + return 1; + + if (FAILED(WasabiApi_LoadDefaults()) || + NULL == OMBROWSERMNGR || + NULL == OMSERVICEMNGR || + NULL == OMUTILITY) + { + WasabiApi_Release(); + return 2; + } + + ServiceHelper_Initialize(); + + if (NULL != WASABI_API_LNG) + { + static wchar_t szDescription[256]; + StringCchPrintf(szDescription, ARRAYSIZE(szDescription), + WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME), + PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR); + plugin.description = (char*)szDescription; + } + + mediaLibrary.library = plugin.hwndLibraryParent; + mediaLibrary.winamp = plugin.hwndWinampParent; + mediaLibrary.instance = plugin.hDllInstance; + + winampVersion = mediaLibrary.GetWinampVersion(); + + omCOM.Publish(); + + Preferences_Register(); + + if (NULL == navigation) + { + if (FAILED(Navigation::CreateInstance(&navigation))) + { + navigation = NULL; + + if (NULL != unloadCallbacks) + { + size_t index = unloadCallbacks->size(); + while(index--) + unloadCallbacks->at(index)(); + delete(unloadCallbacks); + } + + Preferences_Unregister(); + WasabiApi_Release(); + return 3; + } + } + + initConfigCache(); + + Plugin_ExecuteOpenOnce(); + return ML_INIT_SUCCESS; +} + +static void Plugin_Quit() +{ + SaveCache(); + buffer_map.clear(); + + Plugin_UninitializeTimer(); + + if (NULL != navigation) + { + navigation->Finish(); + navigation->Release(); + navigation = NULL; + } + + if (NULL != unloadCallbacks) + { + size_t index = unloadCallbacks->size(); + while(index--) + unloadCallbacks->at(index)(); + delete(unloadCallbacks); + unloadCallbacks = NULL; + } + + Preferences_Unregister(); + + WasabiApi_Release(); +} + +static INT_PTR TitleHook(waHookTitleStructW *hookTitle) +{ + if (NULL == hookTitle || + NULL == hookTitle->filename) + { + return 0; + } + + Nullsoft::Utility::AutoLock lock(urlMapGuard); + // this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much + URLMap::iterator itr; + for (itr=urlMap.begin();itr!=urlMap.end();itr++) + { + if (!_wcsnicmp(hookTitle->filename, itr->url.c_str(), itr->url_wcslen)) + { + if (NULL != hookTitle->title) + StringCchCopy(hookTitle->title, 2048, itr->title.c_str()); + + hookTitle->length = itr->length; + return 1; + } + } + + return 0; +} + +static INT_PTR MetadataHook(extendedFileInfoStructW *hookMetadata) +{ + if (NULL == hookMetadata || + NULL == hookMetadata->filename || + NULL == hookMetadata->metadata) + { + return 0; + } + + Nullsoft::Utility::AutoLock lock(urlMapGuard); + // this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much + MetadataMap::iterator itr; + + for (itr=metadataMap.begin();itr!=metadataMap.end();itr++) + { + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->filename, -1, itr->url.c_str(), - 1) && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->metadata, -1, itr->tag.c_str(), - 1)) + { + StringCchCopy(hookMetadata->ret, hookMetadata->retlen, itr->metadata.c_str()); + return 1; + } + } + return 0; +} + + +static INT_PTR Plugin_MessageProc(int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3) +{ + INT_PTR result = 0; + if (NULL != navigation && + FALSE != navigation->ProcessMessage(msg, param1, param2, param3, &result)) + { + return result; + } + + switch (msg) + { + case ML_IPC_HOOKTITLEW: return TitleHook((waHookTitleStructW *)param1); + case ML_IPC_HOOKEXTINFOW: return MetadataHook((extendedFileInfoStructW *)param1); + case ML_MSG_CONFIG: Preferences_Show(); return TRUE; + } + + return FALSE; +} + + +void Plugin_RegisterUnloadCallback(PLUGINUNLOADCALLBACK callback) +{ + if (NULL == unloadCallbacks) + { + unloadCallbacks = new std::vector(); + if (NULL == unloadCallbacks) + return; + } + unloadCallbacks->push_back(callback); +} + + +extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin() +{ + return &plugin; +} + +#if 0 +extern "C" __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) { + + // prompt to remove our settings with default as no (just incase) + /*if(MessageBoxA(hwndDlg,"Do you also want to remove the saved settings for this plugin?", + plugin.description,MB_YESNO|MB_DEFBUTTON2) == IDYES) + { + WritePrivateProfileString("ml_rg", 0, 0, iniFile); + }*/ + + // also attempt to remove the ReplayGainAnalysis.dll so everything is kept cleaner + /*char path[MAX_PATH] = {0}; + GetModuleFileName(hDllInst, path, MAX_PATH); + PathRemoveFileSpec(path); + PathAppend(path, "ReplayGainAnalysis.dll"); + // if we get a handle then try to lower the handle count so we can delete + HINSTANCE rgLib = GetModuleHandle(path); + if(rgLib) + FreeLibrary(rgLib); + DeleteFile(path);*/ + + // allow an on-the-fly removal (since we've got to be with a compatible client build) + return ML_PLUGIN_UNINSTALL_NOW; + } +#endif \ No newline at end of file -- cgit