1
0

ml_plg.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. #define PLUGIN_VER L"1.81"
  2. #define FORCED_REBUILD_VERSION 1 // When changing this be sure to update 'forcedRebuildVersion' logic if no breaking changes are introduced in the new version
  3. #include "api__ml_plg.h"
  4. #include "../../General/gen_ml/ml.h"
  5. #include "resource.h"
  6. #include "main.h"
  7. #include "../winamp/wa_ipc.h"
  8. #include "../winamp/ipc_pe.h"
  9. #include "../nu/MediaLibraryInterface.h"
  10. #include "../nu/AutoChar.h"
  11. #include "../nu/ns_wc.h"
  12. #include <api/service/waservicefactory.h>
  13. #include "playlist.h"
  14. #include "../Agave/Language/api_language.h"
  15. #include "resource.h"
  16. //#include "mldbcallbacks.h"
  17. #include "../../General/gen_ml/menufucker.h"
  18. #include "../nu/ServiceWatcher.h"
  19. //#include "api_playlist_generator.h"
  20. #include "../nu/Singleton.h"
  21. #include "PlaylistGeneratorApi.h"
  22. //#include "wacmldbcallbacks.h"
  23. #include <strsafe.h> // make sure this always gets #include'd last
  24. // For the playlist generator API
  25. static PlaylistGeneratorAPI playlistGeneratorAPI;
  26. static SingletonServiceFactory<api_playlist_generator, PlaylistGeneratorAPI> playlistGeneratorFactory;
  27. api_threadpool *WASABI_API_THREADPOOL = 0;
  28. api_application *WASABI_API_APP = 0;
  29. api_playlistmanager *AGAVE_API_PLAYLISTMGR = 0;
  30. api_language *WASABI_API_LNG = 0;
  31. api_config *AGAVE_API_CONFIG=0;
  32. api_gracenote *AGAVE_API_GRACENOTE=0;
  33. api_decodefile *AGAVE_API_DECODE=0;
  34. api_metadata *AGAVE_API_METADATA=0;
  35. api_mldb *AGAVE_API_MLDB = 0;
  36. api_playlists *AGAVE_API_PLAYLISTS = 0;
  37. api_stats *AGAVE_API_STATS = 0;
  38. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  39. api_syscb *WASABI_API_SYSCB=0;
  40. class MLDBWatcher : public ServiceWatcherSingle
  41. {
  42. public:
  43. void OnDeregister()
  44. {
  45. StopScan();
  46. }
  47. };
  48. static MLDBWatcher mldbWatcher;
  49. extern winampMediaLibraryPlugin plugin;
  50. template <class api_T>
  51. void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
  52. {
  53. if (plugin.service)
  54. {
  55. waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
  56. if (factory)
  57. api_t = reinterpret_cast<api_T *>( factory->getInterface() );
  58. }
  59. }
  60. template <class api_T>
  61. void ServiceRelease(api_T *&api_t, GUID factoryGUID_t)
  62. {
  63. if (plugin.service && api_t)
  64. {
  65. waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
  66. if (factory)
  67. factory->releaseInterface(api_t);
  68. }
  69. api_t = NULL;
  70. }
  71. static HWND winampPlaylist;
  72. static int ML_IPC_MENUFUCKER_BUILD;
  73. static int ML_IPC_MENUFUCKER_RESULT;
  74. bool pluginEnabled = true;
  75. int scanMode = 0; // 0 = not inited, 1 = on start, 2 = on use
  76. int plLengthType = PL_ITEMS; // 0 = not inited, 1 = items (# of), 2 = length (minutes), 3 = size (kilobytes)
  77. //int plLength = 20; // Length of playlist, used for either # of items or for minutes target of the playlist
  78. int plItems = 20; // Number of desired items in the playlist
  79. int plMinutes = 60; // Number of desired minutes in the playlist
  80. int plMegabytes = 650; // Size of the desired playlist in kilobytes
  81. int multipleArtists = FALSE; // Generate tracks from the same artists in a single playlist
  82. int multipleAlbums = FALSE; // Generate tracks from the same albums in a single playlist
  83. int useSeed = TRUE; // Put the seed track into the generated playlist
  84. int useMLQuery = FALSE; // Use a custom query against the media library database to post process the results
  85. wchar_t mlQuery[MAX_ML_QUERY_SIZE] = {0}; // Storage for the custom query
  86. int forcedRebuildVersion = 0; // Stores the ID of a forced rebuild when upgrading, 0 is never reset, 1 reset with ml_plg rewrite, 2+ and on are reserved for future scenarios
  87. ThreadID *plg_thread=0; // Thread ID for the single gracenote thread that we always make API calls on.
  88. bool reset_db_flag = false; // Flag that gets set whenever the DB needs to be reset before a scan.
  89. bool run_full_scan_flag = true; // Flag that gets set whenever there are media library changes so step 4 (pass 2) can be rerun for any changed files
  90. volatile bool run_pass2_flag = false; // Flag that gets set whenever there are media library changes so step 4 (pass 2) can be rerun for any changed files
  91. void WriteIntToIni(const char *key, const int value)
  92. {
  93. char buf[32] = {0};
  94. _itoa(value, buf, 10);
  95. WritePrivateProfileStringA("ml_plg", key, buf, mediaLibrary.GetWinampIni());
  96. }
  97. // BE CAREFULL! Using this could potentially internationalize floats on some versions of windows eg. '1,6' instead of '1.6'
  98. void WriteFloatToIni(const char *key, const float value)
  99. {
  100. char buf[32] = {0};
  101. StringCchPrintfA(buf, 32, "%.2f", value);
  102. WritePrivateProfileStringA("ml_plg", key, buf, mediaLibrary.GetWinampIni());
  103. }
  104. int Init()
  105. {
  106. mediaLibrary.library = plugin.hwndLibraryParent;
  107. mediaLibrary.winamp = plugin.hwndWinampParent;
  108. mediaLibrary.instance = plugin.hDllInstance;
  109. ServiceBuild(WASABI_API_SYSCB, syscbApiServiceGuid);
  110. ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
  111. ServiceBuild(AGAVE_API_PLAYLISTMGR, api_playlistmanagerGUID);
  112. ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
  113. ServiceBuild(AGAVE_API_DECODE, decodeFileGUID);
  114. ServiceBuild(AGAVE_API_GRACENOTE, gracenoteApiGUID);
  115. ServiceBuild(AGAVE_API_METADATA, api_metadataGUID);
  116. ServiceBuild(AGAVE_API_PLAYLISTS, api_playlistsGUID);
  117. ServiceBuild(AGAVE_API_STATS, AnonymousStatsGUID);
  118. ServiceBuild(WASABI_API_LNG, languageApiGUID);
  119. ServiceBuild(WASABI_API_THREADPOOL, ThreadPoolGUID);
  120. playlistGeneratorFactory.Register(plugin.service, &playlistGeneratorAPI);
  121. if (WASABI_API_THREADPOOL)
  122. plg_thread = WASABI_API_THREADPOOL->ReserveThread(api_threadpool::FLAG_REQUIRE_COM_STA);
  123. if (!plg_thread)
  124. return ML_INIT_FAILURE; // if we weren't able to get a thread from the threadpool, bail out
  125. // no guarantee that AGAVE_API_MLDB will be available yet, so we'll start a watcher for it
  126. mldbWatcher.WatchWith(plugin.service);
  127. mldbWatcher.WatchFor(&AGAVE_API_MLDB, mldbApiGuid);
  128. WASABI_API_SYSCB->syscb_registerCallback(&mldbWatcher);
  129. // need to have this initialised before we try to do anything with localisation features
  130. WASABI_API_START_LANG(plugin.hDllInstance,MlPlgLangGUID);
  131. ML_IPC_MENUFUCKER_BUILD = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"menufucker_build", IPC_REGISTER_WINAMP_IPCMESSAGE);
  132. ML_IPC_MENUFUCKER_RESULT = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"menufucker_result", IPC_REGISTER_WINAMP_IPCMESSAGE);
  133. winampPlaylist = (HWND)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND);
  134. static wchar_t szDescription[256];
  135. StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
  136. WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PLAYLIST_GENERATOR), PLUGIN_VER);
  137. plugin.description = (char*)szDescription;
  138. // Load variables from winamp.ini
  139. scanMode = GetPrivateProfileInt(L"ml_plg", L"scanmode", 0, mediaLibrary.GetWinampIniW());
  140. pluginEnabled = GetPrivateProfileInt(L"ml_plg", L"enable", 1, mediaLibrary.GetWinampIniW())!=0;
  141. multipleArtists = GetPrivateProfileInt(L"ml_plg", L"multipleArtists", multipleArtists, mediaLibrary.GetWinampIniW());
  142. multipleAlbums = GetPrivateProfileInt(L"ml_plg", L"multipleAlbums", multipleAlbums, mediaLibrary.GetWinampIniW());
  143. plLengthType = GetPrivateProfileInt(L"ml_plg", L"plLengthType", plLengthType, mediaLibrary.GetWinampIniW());
  144. plItems = GetPrivateProfileInt(L"ml_plg", L"plItems", plItems, mediaLibrary.GetWinampIniW());
  145. plMinutes = GetPrivateProfileInt(L"ml_plg", L"plMinutes", plMinutes, mediaLibrary.GetWinampIniW());
  146. plMegabytes = GetPrivateProfileInt(L"ml_plg", L"plMegabytes", plMegabytes, mediaLibrary.GetWinampIniW());
  147. useSeed = GetPrivateProfileInt(L"ml_plg", L"useSeed", useSeed, mediaLibrary.GetWinampIniW());
  148. useMLQuery = GetPrivateProfileInt(L"ml_plg", L"useMLQuery", useMLQuery, mediaLibrary.GetWinampIniW());
  149. char temp[MAX_ML_QUERY_SIZE] = {0};
  150. GetPrivateProfileStringA("ml_plg", "mlQuery", DEFAULT_ML_QUERY, temp, sizeof(temp), mediaLibrary.GetWinampIni());
  151. MultiByteToWideCharSZ(CP_UTF8, 0, temp, -1, mlQuery, sizeof(mlQuery)/sizeof(mlQuery[0]));
  152. //GetPrivateProfileStringA("ml_plg", "forcedRebuildVersion", "", temp, sizeof(temp), mediaLibrary.GetWinampIni());
  153. //forcedRebuildVersion = (float)atof(temp);
  154. forcedRebuildVersion = GetPrivateProfileIntA("ml_plg","forcedRebuildVersion", forcedRebuildVersion, mediaLibrary.GetWinampIni());
  155. // Here we check if the person is upgrading from the old ml_plg, if that value is less than our current version then we need to force a rebuild
  156. if (forcedRebuildVersion < FORCED_REBUILD_VERSION/*atof(PLUGIN_VER)*/) // NOTE: Hard code this to a version if no breaking changes were made
  157. { // Otherwise there will be a forced reset every time version is incremented
  158. reset_db_flag = true;
  159. //ResetDBOnThread(true);
  160. }
  161. forcedRebuildVersion = FORCED_REBUILD_VERSION; //(float)atof(PLUGIN_VER);
  162. if(scanMode == 1) // If scanmode is set to rescan on winamp launch
  163. WASABI_API_CREATEDIALOGPARAMW(IDD_NAG, plugin.hwndWinampParent, BGScanProcedure, 1); // 1 means silent!
  164. return ML_INIT_SUCCESS;
  165. }
  166. void Quit()
  167. {
  168. StopScan();
  169. HANDLE wait_event = CreateEvent(NULL, FALSE, FALSE, 0);
  170. WASABI_API_THREADPOOL->RunFunction(plg_thread, ShutdownScanner, (void *)wait_event, 0, api_threadpool::FLAG_REQUIRE_COM_STA);
  171. WaitForSingleObject(wait_event, INFINITE);
  172. mldbWatcher.StopWatching();
  173. WASABI_API_THREADPOOL->ReleaseThread(plg_thread);
  174. ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
  175. ServiceRelease(AGAVE_API_PLAYLISTMGR, api_playlistmanagerGUID);
  176. ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
  177. ServiceRelease(AGAVE_API_DECODE, decodeFileGUID);
  178. ServiceRelease(AGAVE_API_GRACENOTE, gracenoteApiGUID);
  179. ServiceRelease(AGAVE_API_METADATA, api_metadataGUID);
  180. ServiceRelease(AGAVE_API_MLDB, mldbApiGuid);
  181. ServiceRelease(AGAVE_API_PLAYLISTS, api_playlistsGUID);
  182. ServiceRelease(AGAVE_API_STATS, AnonymousStatsGUID);
  183. ServiceRelease(WASABI_API_THREADPOOL, ThreadPoolGUID);
  184. playlistGeneratorFactory.Deregister(plugin.service);
  185. //WASABI_API_SYSCB->syscb_deregisterCallback(&IDscanner);
  186. }
  187. static void FixAmps(wchar_t *str, size_t len)
  188. {
  189. size_t realSize = 0;
  190. size_t extra = 0;
  191. wchar_t *itr = str;
  192. while (itr && *itr)
  193. {
  194. if (*itr == L'&')
  195. extra++;
  196. itr++;
  197. realSize++;
  198. }
  199. extra = min(len - (realSize + 1), extra);
  200. while (extra)
  201. {
  202. str[extra+realSize] = str[realSize];
  203. if (str[realSize] == L'&')
  204. {
  205. extra--;
  206. str[extra+realSize] = L'&';
  207. }
  208. realSize--;
  209. }
  210. }
  211. static void FixStrForMenu(wchar_t *str, size_t len)
  212. {
  213. FixAmps(str,len);
  214. }
  215. // Triggered once some seed tracks are selected and added by the user
  216. HWND SongsSelected(void)
  217. {
  218. // I know this function is a one-liner but it may not be the case forever
  219. //WASABI_API_CREATEDIALOG(IDD_GENERATE, GetDesktopWindow(), GenerateProcedure);
  220. return WASABI_API_CREATEDIALOGW(IDD_GENERATE, plugin.hwndLibraryParent, GenerateProcedure);
  221. }
  222. // Display the warning message that the current file is not in ML so it cannot be used as a seed track
  223. void NotInMLWarning(const wchar_t *filename)
  224. {
  225. wchar_t message[MAX_PATH + 256] = {0};
  226. StringCchPrintfW(message, MAX_PATH + 256, WASABI_API_LNGSTRINGW(IDS_CANT_USE_SEED), filename);
  227. MessageBoxW(plugin.hwndLibraryParent, message, (LPWSTR)plugin.description, MB_OK| MB_ICONINFORMATION);
  228. }
  229. void MultipleInstancesWarning(void)
  230. {
  231. MessageBoxW(plugin.hwndLibraryParent, WASABI_API_LNGSTRINGW(IDS_THERE_CAN_BE_ONLY_ONE), (LPWSTR)plugin.description, MB_OK | MB_ICONINFORMATION);
  232. }
  233. // Add seed tracks from main media library view
  234. int AddSeedTracks(menufucker_t *mf)
  235. {
  236. const int count = mf->extinf.mediaview.items->Size;
  237. int position = ListView_GetNextItem(mf->extinf.mediaview.list, -1, LVNI_SELECTED); // Start the search from -1 so that we dont ignore the 0th selection
  238. while (position >= 0 && position < count)
  239. {
  240. wchar_t winamp_title[MAX_TITLE_SIZE] = {0};
  241. itemRecordW *item = &mf->extinf.mediaview.items->Items[position];
  242. if (item)
  243. {
  244. GetTitleFormattingML(item->filename, item, winamp_title, MAX_TITLE_SIZE);
  245. seedPlaylist.AppendWithInfo(item->filename, winamp_title, item->length * 1000, item->filesize * 1024);
  246. AGAVE_API_MLDB->FreeRecord(item);
  247. }
  248. position = ListView_GetNextItem(mf->extinf.mediaview.list, position, LVNI_SELECTED);
  249. }
  250. return true;
  251. }
  252. // Add seed tracks from a media library playlist
  253. int AddSeedTracksMlPlaylist(menufucker_t *mf)
  254. {
  255. int position = ListView_GetNextItem(mf->extinf.mlplaylist.list, -1, LVNI_SELECTED); // Start the search from -1 so that we dont ignore the 0th selection
  256. while (position >= 0)
  257. {
  258. wchar_t filename[MAX_PATH] = {0};
  259. mf->extinf.mlplaylist.pl->GetItem(position, filename, MAX_PATH);
  260. itemRecordW *item = AGAVE_API_MLDB->GetFile(filename);
  261. if (item)
  262. {
  263. wchar_t winamp_title[MAX_TITLE_SIZE] = {0};
  264. GetTitleFormattingML(item->filename, item, winamp_title, MAX_TITLE_SIZE);
  265. seedPlaylist.AppendWithInfo(item->filename, winamp_title, item->length * 1000, item->filesize * 1024);
  266. AGAVE_API_MLDB->FreeRecord(item);
  267. }
  268. position = ListView_GetNextItem(mf->extinf.mlplaylist.list, position, LVNI_SELECTED);
  269. }
  270. return true;
  271. }
  272. // Add tracks from the winamp playlist
  273. int AddSeedTracksPlaylist(menufucker_t *mf, int first_selection)
  274. {
  275. bool isSuccess = true;
  276. int position = first_selection;
  277. while (position >= 0)
  278. {
  279. wchar_t winamp_title[MAX_TITLE_SIZE] = {0};
  280. fileinfoW inf={0};
  281. inf.index = position;
  282. SendMessage(winampPlaylist,WM_WA_IPC,IPC_PE_GETINDEXINFOW_INPROC,(LPARAM)&inf);
  283. itemRecordW *item = AGAVE_API_MLDB->GetFile(inf.file);
  284. if (item)
  285. {
  286. GetTitleFormattingML(inf.file, item, winamp_title, MAX_TITLE_SIZE);
  287. seedPlaylist.AppendWithInfo(item->filename, winamp_title, item->length * 1000, item->filesize * 1024);
  288. AGAVE_API_MLDB->FreeRecord(item);
  289. }
  290. else
  291. {
  292. NotInMLWarning(inf.file); // Popup to warn that its not in the ML
  293. }
  294. position = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, position, IPC_PLAYLIST_GET_NEXT_SELECTED);
  295. }
  296. if (seedPlaylist.GetNumItems() == 0)
  297. isSuccess = false;
  298. return isSuccess;
  299. }
  300. // Add a single seed track from the now playing song ticker
  301. int AddSeedTrack(const wchar_t *filename)
  302. {
  303. bool isSuccess = true;
  304. if (filename)
  305. {
  306. wchar_t winamp_title[MAX_TITLE_SIZE] = {0};
  307. itemRecordW *item = AGAVE_API_MLDB->GetFile(filename);
  308. if (item)
  309. {
  310. GetTitleFormattingML(filename, item, winamp_title, MAX_TITLE_SIZE);
  311. seedPlaylist.AppendWithInfo(filename, winamp_title, item->length * 1000, item->filesize * 1024);
  312. AGAVE_API_MLDB->FreeRecord(item);
  313. }
  314. else
  315. {
  316. NotInMLWarning(filename); // Popup to warn that its not in the ML
  317. }
  318. }
  319. else
  320. {
  321. NotInMLWarning(filename); // Popup to warn that its not in the ML
  322. }
  323. if (seedPlaylist.GetNumItems() == 0)
  324. isSuccess = false;
  325. return isSuccess;
  326. }
  327. void WriteSettingsToIni(HWND hwndDlg)
  328. {
  329. /*char buf[32] = {0};
  330. StringCchPrintfA(buf, 32, "%d", plLengthType);
  331. WritePrivateProfileStringA("ml_plg","plLengthType",buf,mediaLibrary.GetWinampIni());*/
  332. WriteIntToIni("plLengthType", plLengthType);
  333. WriteIntToIni("plItems", plItems);
  334. WriteIntToIni("plMinutes", plMinutes);
  335. WriteIntToIni("plMegabytes", plMegabytes);
  336. WriteIntToIni("forcedRebuildVersion", forcedRebuildVersion);
  337. //WriteFloatToIni("forcedRebuildVersion", forcedRebuildVersion);
  338. WriteIntToIni("multipleArtists", multipleArtists);
  339. WriteIntToIni("multipleAlbums", multipleAlbums);
  340. WriteIntToIni("useSeed", useSeed);
  341. WriteIntToIni("useMLQuery", useMLQuery);
  342. /*multipleArtists = IsDlgButtonChecked(hwndDlg,IDC_CHECK_MULTIPLE_ARTISTS);
  343. WritePrivateProfileStringA("ml_plg","multipleArtists",multipleArtists?"1":"0",mediaLibrary.GetWinampIni());
  344. multipleAlbums = IsDlgButtonChecked(hwndDlg,IDC_CHECK_MULTIPLE_ALBUMS);
  345. WritePrivateProfileStringA("ml_plg","multipleAlbums",multipleAlbums?"1":"0",mediaLibrary.GetWinampIni());
  346. useSeed = IsDlgButtonChecked(hwndDlg,IDC_CHECK_USE_SEED);
  347. WritePrivateProfileStringA("ml_plg","useSeed",useSeed?"1":"0",mediaLibrary.GetWinampIni());
  348. useMLQuery = IsDlgButtonChecked(hwndDlg,IDC_CHECK_ML_QUERY);
  349. WritePrivateProfileStringA("ml_plg","useMLQuery", useMLQuery ? "1" : "0",mediaLibrary.GetWinampIni());*/
  350. //WritePrivateProfileStringW(L"ml_plg",L"mlQuery", mlQuery ,mediaLibrary.GetWinampIniW());
  351. WritePrivateProfileStringA("ml_plg", "mlQuery", AutoChar(mlQuery, CP_UTF8), mediaLibrary.GetWinampIni());
  352. }
  353. static bool IsInternetAvailable()
  354. {
  355. return !!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_INETAVAILABLE);
  356. }
  357. INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3)
  358. {
  359. static int mymenuid=0;
  360. if(message_type == ML_IPC_MENUFUCKER_BUILD && pluginEnabled && IsInternetAvailable())
  361. {
  362. menufucker_t* mf = (menufucker_t*)param1;
  363. wchar_t str[100] = {0}, str2[64] = {0};
  364. MENUITEMINFOW mii =
  365. {
  366. sizeof(MENUITEMINFOW),
  367. MIIM_TYPE | MIIM_ID,
  368. MFT_STRING,
  369. MFS_ENABLED,
  370. (UINT)mf->nextidx,
  371. 0
  372. };
  373. mymenuid = mf->nextidx;
  374. mf->nextidx++;
  375. if(mf->type == MENU_MEDIAVIEW)
  376. {
  377. int n = ListView_GetSelectionMark(mf->extinf.mediaview.list);
  378. if(n == -1)
  379. {
  380. mymenuid=0;
  381. return 0;
  382. }
  383. itemRecordW * ice = &mf->extinf.mediaview.items->Items[n];
  384. if(!ice->title || !ice->title[0])
  385. {
  386. mymenuid=0;
  387. return 0;
  388. }
  389. int len = lstrlenW(ice->title);
  390. if (len > 39)
  391. {
  392. StringCchPrintfW(str2, 40, L"%.36s...", ice->title);
  393. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);
  394. }
  395. else
  396. {
  397. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), ice->title);
  398. }
  399. FixStrForMenu(str,100);
  400. mii.dwTypeData = str;
  401. mii.cch = (UINT)wcslen(str);
  402. if(!InsertMenuItem(mf->menu,0xdeadbeef,FALSE,&mii))
  403. {
  404. InsertMenuItem(mf->menu,40012,FALSE,&mii);
  405. mii.wID = 0xdeadbeef;
  406. mii.fType = MFT_SEPARATOR;
  407. InsertMenuItem(mf->menu,40012,FALSE,&mii);
  408. }
  409. }
  410. else if(mf->type == MENU_MLPLAYLIST)
  411. {
  412. int n = ListView_GetSelectionMark(mf->extinf.mlplaylist.list);
  413. if(n == -1)
  414. {
  415. mymenuid=0;
  416. return 0;
  417. }
  418. wchar_t filename[MAX_PATH] = {0}, title[75] = {0};
  419. mf->extinf.mlplaylist.pl->GetItem(n,filename,MAX_PATH);
  420. AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"title", title, 75);
  421. if(!title[0])
  422. {
  423. mymenuid=0;
  424. return 0;
  425. }
  426. int len = lstrlenW(title);
  427. if (len > 39)
  428. {
  429. StringCchPrintfW(str2, 40, L"%.36s...", title);
  430. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);
  431. }
  432. else
  433. {
  434. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), title);
  435. }
  436. FixStrForMenu(str,100);
  437. mii.dwTypeData = str;
  438. mii.cch = (UINT)wcslen(str);
  439. InsertMenuItem(mf->menu,3,TRUE,&mii);
  440. mii.wID = 0xdeadc0de;
  441. mii.fType = MFT_SEPARATOR;
  442. InsertMenuItem(mf->menu,3,TRUE,&mii);
  443. }
  444. else if(mf->type == MENU_PLAYLIST)
  445. {
  446. int n = (int)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,-1,IPC_PLAYLIST_GET_NEXT_SELECTED);
  447. if(n == -1) {
  448. mymenuid=0;
  449. return 0;
  450. }
  451. fileinfoW inf={0};
  452. inf.index = n;
  453. SendMessage(winampPlaylist,WM_WA_IPC,IPC_PE_GETINDEXINFOW_INPROC,(LPARAM)&inf);
  454. wchar_t title[75] = {0};
  455. AGAVE_API_METADATA->GetExtendedFileInfo(inf.file, L"title", title, 75);
  456. if(!title[0])
  457. {
  458. mymenuid=0;
  459. return 0;
  460. }
  461. int len = lstrlenW(title);
  462. if (len > 39)
  463. {
  464. StringCchPrintfW(str2, 40, L"%.36s...", title);
  465. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);
  466. }
  467. else
  468. {
  469. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), title);
  470. }
  471. FixStrForMenu(str,100);
  472. mii.dwTypeData = str;
  473. mii.cch = (UINT)wcslen(str);
  474. InsertMenuItem(mf->menu,40470/*40208*/,FALSE,&mii);
  475. mii.wID = 0xdeadc0de;
  476. mii.fType = MFT_SEPARATOR;
  477. InsertMenuItem(mf->menu,40470/*40208*/,FALSE,&mii);
  478. }
  479. else if (mf->type == MENU_SONGTICKER)
  480. {
  481. wchar_t * file = (wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME);
  482. wchar_t title[75] = {0};
  483. AGAVE_API_METADATA->GetExtendedFileInfo(file, L"title", title, 75);
  484. if(!title[0])
  485. {
  486. mymenuid=0;
  487. return 0;
  488. }
  489. int len = lstrlenW(title);
  490. if (len > 39)
  491. {
  492. StringCchPrintfW(str2, 40, L"%.36s...", title);
  493. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), str2);
  494. }
  495. else
  496. {
  497. StringCchPrintfW(str, 100, WASABI_API_LNGSTRINGW(IDS_PLAY_TRACKS_SIMILAR_TO), title);
  498. }
  499. FixStrForMenu(str,100);
  500. mii.dwTypeData = str;
  501. mii.cch = (UINT)wcslen(str);
  502. InsertMenuItem(mf->menu,0,TRUE,&mii);
  503. }
  504. }
  505. else if(message_type == ML_IPC_MENUFUCKER_RESULT && mymenuid != 0 && pluginEnabled)
  506. {
  507. menufucker_t* mf = (menufucker_t*)param1;
  508. DeleteMenu(mf->menu,mymenuid,MF_BYCOMMAND);
  509. if(mf->type == MENU_PLAYLIST || mf->type == MENU_MLPLAYLIST) DeleteMenu(mf->menu,0xdeadc0de,MF_BYCOMMAND);
  510. if(param2 == mymenuid && mymenuid != 0)
  511. {
  512. if(mf->type == MENU_MEDIAVIEW) // Main Media Library View
  513. {
  514. int n = ListView_GetSelectionMark(mf->extinf.mediaview.list);
  515. if(n == -1)
  516. {
  517. mymenuid=0;
  518. return 0;
  519. }
  520. if (hwndDlgCurrent) // Warn if trying to open two seperate playlist generators
  521. MultipleInstancesWarning();
  522. else
  523. {
  524. if (AddSeedTracks(mf)) // Make sure that we added the seed tracks successfully
  525. SongsSelected();
  526. }
  527. }
  528. else if(mf->type == MENU_MLPLAYLIST) // Media library playlist view
  529. {
  530. // Check to see if anything is selected
  531. int n = ListView_GetSelectionMark(mf->extinf.mlplaylist.list);
  532. if(n == -1)
  533. {
  534. mymenuid=0;
  535. return 0;
  536. }
  537. if (hwndDlgCurrent) // Warn if trying to open two seperate playlist generators
  538. MultipleInstancesWarning();
  539. else
  540. {
  541. if (AddSeedTracksMlPlaylist(mf)) // Make sure that we added the seed tracks successfully
  542. SongsSelected();
  543. }
  544. }
  545. else if(mf->type == MENU_PLAYLIST) // Main window playlist
  546. {
  547. // Check to see if anything is selected
  548. int n = (int)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,-1,IPC_PLAYLIST_GET_NEXT_SELECTED);
  549. if(n == -1)
  550. {
  551. mymenuid=0;
  552. return 0;
  553. }
  554. if (hwndDlgCurrent) // Warn if trying to open two seperate playlist generators
  555. MultipleInstancesWarning();
  556. else
  557. {
  558. if (AddSeedTracksPlaylist(mf, n)) // Make sure that we added the seed tracks successfully
  559. SongsSelected();
  560. }
  561. }
  562. else if(mf->type == MENU_SONGTICKER) // Current playing track in the song ticker
  563. {
  564. wchar_t * file = (wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME);
  565. if (file)
  566. {
  567. if (hwndDlgCurrent) // Warn if trying to open two seperate playlist generators
  568. MultipleInstancesWarning();
  569. else
  570. {
  571. if (AddSeedTrack(file)) // Make sure that we added the seed tracks successfully
  572. SongsSelected();
  573. }
  574. }
  575. }
  576. }
  577. mymenuid=0;
  578. }
  579. else switch (message_type)
  580. {
  581. case ML_MSG_CONFIG:
  582. {
  583. HWND parent = (HWND)param1;
  584. WASABI_API_DIALOGBOXW(IDD_PREFS, parent, PrefsProcedure);
  585. return TRUE;
  586. }
  587. break;
  588. }
  589. return 0;
  590. }
  591. extern "C" winampMediaLibraryPlugin plugin =
  592. {
  593. MLHDR_VER,
  594. "nullsoft(ml_plg.dll)", // name filled in later
  595. Init,
  596. Quit,
  597. MessageProc,
  598. 0,
  599. 0,
  600. 0,
  601. };
  602. extern "C"
  603. {
  604. __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
  605. {
  606. return &plugin;
  607. }
  608. __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
  609. // prompt to remove our settings with default as no (just incase)
  610. static wchar_t title[256];
  611. StringCchPrintf(title, ARRAYSIZE(title),
  612. WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PLAYLIST_GENERATOR), PLUGIN_VER);
  613. if(MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS),
  614. title,MB_YESNO|MB_DEFBUTTON2) == IDYES)
  615. {
  616. WritePrivateProfileStringW(L"ml_plg",0,0,mediaLibrary.GetWinampIniW());
  617. }
  618. // allow an on-the-fly removal (since we've got to be with a compatible client build)
  619. return ML_PLUGIN_UNINSTALL_NOW;
  620. }
  621. };