aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_online/Main.cpp
diff options
context:
space:
mode:
authorJean-Francois Mauguit <jfmauguit@mac.com>2024-09-24 09:03:25 -0400
committerGitHub <noreply@github.com>2024-09-24 09:03:25 -0400
commitbab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_online/Main.cpp
parent4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff)
parent20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff)
downloadwinamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/Plugins/Library/ml_online/Main.cpp')
-rw-r--r--Src/Plugins/Library/ml_online/Main.cpp599
1 files changed, 599 insertions, 0 deletions
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 <vector>
+#include "../nu/nonewthrow.c"
+#include "../nu/ConfigCOM.h"
+
+#include "BufferCache.h"
+#include "OMCOM.h"
+#include "JNetCom.h" // for buffer_map
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+
+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<PLUGINUNLOADCALLBACK> *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<PLUGINTIMERREC> 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<PLUGINUNLOADCALLBACK>();
+ 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