Main.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. #include "api__ml_history.h"
  2. #include "main.h"
  3. #include "..\..\General\gen_ml/config.h"
  4. #include "HistoryAPIFactory.h"
  5. #include "JSAPI2_Creator.h"
  6. #include "..\..\General\gen_ml/ml_ipc_0313.h"
  7. #include "../nu/AutoCharFn.h"
  8. #include <strsafe.h>
  9. #define LOCAL_WRITE_VER L"2.03"
  10. // wasabi based services for localisation support
  11. api_language *WASABI_API_LNG = 0;
  12. api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = 0;
  13. JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = 0;
  14. api_application *WASABI_API_APP=0;
  15. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  16. static HistoryAPIFactory historyAPIFactory;
  17. static JSAPI2Factory jsapi2Factory;
  18. static int Init();
  19. static void Quit();
  20. static INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3);
  21. prefsDlgRecW preferences = {0};
  22. wchar_t g_tableDir[MAX_PATH] = {0};
  23. extern "C" winampMediaLibraryPlugin plugin =
  24. {
  25. MLHDR_VER,
  26. "nullsoft(ml_history.dll)",
  27. Init,
  28. Quit,
  29. MessageProc,
  30. 0,
  31. 0,
  32. 0,
  33. };
  34. // Delay load library control << begin >>
  35. #include <delayimp.h>
  36. #pragma comment(lib, "delayimp")
  37. bool nde_error = false;
  38. FARPROC WINAPI FailHook(unsigned dliNotify, PDelayLoadInfo pdli)
  39. {
  40. nde_error = true;
  41. return 0;
  42. }
  43. /*
  44. extern "C"
  45. {
  46. PfnDliHook __pfnDliFailureHook2 = FailHook;
  47. }
  48. */
  49. // Delay load library control << end >>
  50. int Init()
  51. {
  52. InitializeCriticalSection(&g_db_cs);
  53. g_db = NULL;
  54. g_table = NULL;
  55. mediaLibrary.library = plugin.hwndLibraryParent;
  56. mediaLibrary.winamp = plugin.hwndWinampParent;
  57. mediaLibrary.instance = plugin.hDllInstance;
  58. wchar_t configName[ MAX_PATH ] = { 0 };
  59. const wchar_t *dir = mediaLibrary.GetIniDirectoryW();
  60. if ( (INT_PTR)( dir ) < 65536 )
  61. return ML_INIT_FAILURE;
  62. PathCombineW( g_tableDir, dir, L"Plugins" );
  63. PathCombineW( configName, g_tableDir, L"gen_ml.ini" );
  64. g_config = new C_Config( configName );
  65. CreateDirectoryW( g_tableDir, NULL );
  66. PathCombineW( g_tableDir, g_tableDir, L"ml" );
  67. CreateDirectoryW( g_tableDir, NULL );
  68. // loader so that we can get the localisation service api for use
  69. waServiceFactory *sf = plugin.service->service_getServiceByGuid( languageApiGUID );
  70. if ( sf )
  71. WASABI_API_LNG = reinterpret_cast<api_language *>( sf->getInterface() );
  72. sf = plugin.service->service_getServiceByGuid( JSAPI2::api_securityGUID );
  73. if ( sf )
  74. AGAVE_API_JSAPI2_SECURITY = reinterpret_cast<JSAPI2::api_security *>( sf->getInterface() );
  75. sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid );
  76. if ( sf )
  77. WASABI_API_APP = reinterpret_cast<api_application *>( sf->getInterface() );
  78. sf = plugin.service->service_getServiceByGuid( ExplorerFindFileApiGUID );
  79. if ( sf )
  80. WASABI_API_EXPLORERFINDFILE = reinterpret_cast<api_explorerfindfile *>( sf->getInterface() );
  81. plugin.service->service_register( &historyAPIFactory );
  82. plugin.service->service_register( &jsapi2Factory );
  83. // need to have this initialised before we try to do anything with localisation features
  84. WASABI_API_START_LANG( plugin.hDllInstance, MlHistoryLangGUID );
  85. g_context_menus2 = WASABI_API_LOADMENUW(IDR_CONTEXTMENUS);
  86. static wchar_t szDescription[256];
  87. StringCchPrintfW( szDescription, ARRAYSIZE( szDescription ), WASABI_API_LNGSTRINGW( IDS_NULLSOFT_HISTORY ), LOCAL_WRITE_VER );
  88. plugin.description = (char*)szDescription;
  89. static wchar_t preferencesName[64] = {0};
  90. preferences.hInst = WASABI_API_LNG_HINST;
  91. preferences.dlgID = IDD_PREFS;
  92. preferences.proc = (void *)PrefsProc;
  93. preferences.name = WASABI_API_LNGSTRINGW_BUF( IDS_HISTORY, preferencesName, 64 );
  94. preferences.where = 6; // media library
  95. SENDWAIPC( plugin.hwndWinampParent, IPC_ADD_PREFS_DLGW, &preferences );
  96. if ( !history_init() )
  97. return ML_INIT_FAILURE;
  98. return ML_INIT_SUCCESS;
  99. }
  100. void Quit()
  101. {
  102. plugin.service->service_deregister(&historyAPIFactory);
  103. plugin.service->service_deregister(&jsapi2Factory);
  104. history_quit();
  105. delete g_config;
  106. DeleteCriticalSection(&g_db_cs);
  107. waServiceFactory *sf = plugin.service->service_getServiceByGuid(languageApiGUID);
  108. if (sf) sf->releaseInterface(WASABI_API_LNG);
  109. sf = plugin.service->service_getServiceByGuid(JSAPI2::api_securityGUID);
  110. if (sf) sf->releaseInterface(AGAVE_API_JSAPI2_SECURITY);
  111. }
  112. static INT_PTR History_OnContextMenu(INT_PTR param1, HWND hHost, POINTS pts)
  113. {
  114. HNAVITEM hItem = (HNAVITEM)param1;
  115. HNAVITEM myItem = MLNavCtrl_FindItemById(plugin.hwndLibraryParent, ml_history_tree);
  116. if (hItem != myItem)
  117. return FALSE;
  118. POINT pt;
  119. POINTSTOPOINT(pt, pts);
  120. if (-1 == pt.x || -1 == pt.y)
  121. {
  122. NAVITEMGETRECT itemRect;
  123. itemRect.fItem = FALSE;
  124. itemRect.hItem = hItem;
  125. if (MLNavItem_GetRect(plugin.hwndLibraryParent, &itemRect))
  126. {
  127. MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2);
  128. pt.x = itemRect.rc.left + 2;
  129. pt.y = itemRect.rc.top + 2;
  130. }
  131. }
  132. HMENU hMenu = WASABI_API_LOADMENUW(IDR_CONTEXTMENUS);
  133. HMENU subMenu = (NULL != hMenu) ? GetSubMenu(hMenu, 1) : NULL;
  134. if (NULL != subMenu)
  135. {
  136. INT r = Menu_TrackPopup(plugin.hwndLibraryParent, subMenu,
  137. TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY |
  138. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
  139. pt.x, pt.y, hHost, NULL);
  140. switch(r)
  141. {
  142. case ID_NAVIGATION_PREFERENCES:
  143. SENDWAIPC(plugin.hwndWinampParent, IPC_OPENPREFSTOPAGE, &preferences);
  144. break;
  145. case ID_NAVIGATION_HELP:
  146. SENDWAIPC(plugin.hwndWinampParent, IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8105304048660-The-Winamp-Media-Library");
  147. break;
  148. }
  149. }
  150. if (NULL != hMenu)
  151. DestroyMenu(hMenu);
  152. return TRUE;
  153. }
  154. void History_StartTracking(const wchar_t *filename, bool resume)
  155. {
  156. KillTimer(plugin.hwndWinampParent, 8082);
  157. if (!resume)
  158. {
  159. free(history_fn);
  160. history_fn = 0;
  161. history_fn_mode = 0;
  162. if (wcsstr(filename, L"://") && _wcsnicmp(filename, L"cda://", 6) && _wcsnicmp(filename, L"file://", 7))
  163. {
  164. history_fn_mode = 1;
  165. }
  166. history_fn = _wcsdup(filename);
  167. }
  168. int timer1 = -1, timer2 = -1, timer3 = -1;
  169. // wait for x seconds
  170. if(g_config->ReadInt(L"recent_wait_secs",0))
  171. {
  172. timer1 = g_config->ReadInt(L"recent_wait_secs_lim",5)*1000;
  173. }
  174. // wait for x percent of the song (approx to a second)
  175. if(g_config->ReadInt(L"recent_wait_percent",0))
  176. {
  177. basicFileInfoStructW bfiW = {0};
  178. bfiW.filename = history_fn;
  179. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&bfiW, IPC_GET_BASIC_FILE_INFOW);
  180. if(bfiW.length > 0)
  181. {
  182. bfiW.length=bfiW.length*1000;
  183. timer2 = (bfiW.length*g_config->ReadInt(L"recent_wait_percent_lim",50))/100;
  184. }
  185. }
  186. // wait for the end of the item (within the last second of the track hopefully)
  187. if(g_config->ReadInt(L"recent_wait_end",0))
  188. {
  189. basicFileInfoStructW bfiW = {0};
  190. bfiW.filename = history_fn;
  191. SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&bfiW, IPC_GET_BASIC_FILE_INFOW);
  192. if(bfiW.length > 0)
  193. {
  194. timer3=(bfiW.length-1)*1000;
  195. }
  196. }
  197. // decide on which playback option will be the prefered duration (smallest wins)
  198. if(timer1 != -1 && timer2 != -1)
  199. {
  200. if(timer1 > timer2)
  201. {
  202. timer = timer2;
  203. }
  204. if(timer2 > timer1)
  205. {
  206. timer = timer1;
  207. }
  208. }
  209. else if(timer1 == -1 && timer2 != -1)
  210. {
  211. timer = timer2;
  212. }
  213. else if(timer2 == -1 && timer1 != -1)
  214. {
  215. timer = timer1;
  216. }
  217. // only track on end of file as very last method
  218. if((timer <= 0) && (timer3 > 0)){ timer = timer3; }
  219. // if no match or something went wrong then try to ensure the default timer value is used
  220. SetTimer(plugin.hwndWinampParent, 8082, ((timer > 0)? timer : 350), NULL);
  221. }
  222. INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3)
  223. {
  224. switch (message_type)
  225. {
  226. case ML_MSG_TREE_ONCREATEVIEW: // param1 = param of tree item, param2 is HWND of parent. return HWND if it is us
  227. return (param1 == ml_history_tree) ? (INT_PTR)onTreeViewSelectChange((HWND)param2) : 0;
  228. case ML_MSG_CONFIG:
  229. mediaLibrary.GoToPreferences((int)preferences._id);
  230. return TRUE;
  231. case ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE:
  232. enqueuedef = (int)param1;
  233. groupBtn = (int)param2;
  234. PostMessage(m_curview_hwnd, WM_APP + 104, param1, param2);
  235. return 0;
  236. case ML_MSG_NAVIGATION_CONTEXTMENU:
  237. return History_OnContextMenu(param1, (HWND)param2, MAKEPOINTS(param3));
  238. case ML_MSG_PLAYING_FILE:
  239. if (param1)
  240. {
  241. int resume = g_config->ReadInt(L"resumeplayback",0);
  242. if(resume)
  243. {
  244. int is_playing = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_ISPLAYING);
  245. //int play_pos = SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GETOUTPUTTIME);
  246. if(is_playing == 1/* && !(play_pos/1000 > 0)*/) //playing, look up last play offset and send seek message
  247. {
  248. wchar_t genre[256]={0};
  249. extendedFileInfoStructW efis={
  250. (wchar_t*)param1,
  251. L"genre",
  252. genre,
  253. ARRAYSIZE(genre),
  254. };
  255. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efis,IPC_GET_EXTENDED_FILE_INFOW);
  256. wchar_t ispodcast[8]={0};
  257. extendedFileInfoStructW efis1={
  258. (wchar_t*)param1,
  259. L"ispodcast",
  260. ispodcast,
  261. ARRAYSIZE(ispodcast),
  262. };
  263. SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efis1,IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE);
  264. if (resume == 2 || (ispodcast[0] && _wtoi(ispodcast) > 0) || (genre[0] && !_wcsicmp(genre, L"podcast")))
  265. {
  266. int offset = retrieve_offset((wchar_t*)param1);
  267. if (offset > 0 && (offset/1000 > 0)) PostMessage(plugin.hwndWinampParent,WM_WA_IPC,offset,IPC_JUMPTOTIME);
  268. }
  269. }
  270. }
  271. History_StartTracking((const wchar_t *)param1, false);
  272. }
  273. break;
  274. case ML_MSG_WRITE_CONFIG:
  275. if(param1)
  276. {
  277. closeDb();
  278. openDb();
  279. }
  280. break;
  281. }
  282. return 0;
  283. }
  284. extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
  285. {
  286. return &plugin;
  287. }