1
0

Main.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. #include "main.h"
  2. #include "Cloud.h"
  3. #include "DownloadThread.h"
  4. #include "OPMLParse.h"
  5. #include "FeedsDialog.h"
  6. #include "DownloadsParse.h"
  7. #include "XMLWriter.h"
  8. #include "FeedParse.h"
  9. #include "DownloadsDialog.h"
  10. #include "Preferences.h"
  11. #include "..\..\General\gen_ml/ml.h"
  12. #include "Defaults.h"
  13. #include "Wire.h"
  14. #include "..\..\General\gen_ml/ml_ipc_0313.h"
  15. #include "RSSCOM.h"
  16. #include "api__ml_wire.h"
  17. #include "Downloaded.h"
  18. #include "DownloadStatus.h"
  19. #include "Factory.h"
  20. #include "JSAPI2_Creator.h"
  21. #include "./navigation.h"
  22. #include <strsafe.h>
  23. #include "PCastFactory.h"
  24. Cloud cloud;
  25. WireManager channelMgr;
  26. int treeId = 0, allId = 0
  27. #if 0
  28. , discoverId = 0
  29. #endif
  30. ;
  31. MLTREEITEMW downloadsTree;
  32. wchar_t downloadsStr[64] = {0}, *ml_cfg = 0,
  33. feedsXmlFileName[1024] = {0},
  34. feedsXmlFileNameBackup[1024] = {0},
  35. rssXmlFileName[1024] = {0},
  36. rssXmlFileNameBackup[1024] = {0};
  37. ATOM VIEWPROP = 0;
  38. api_downloadManager *WAC_API_DOWNLOADMANAGER = 0;
  39. static int Init();
  40. static void Quit();
  41. static INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3);
  42. DWORD threadStorage=TLS_OUT_OF_INDEXES;
  43. extern "C" winampMediaLibraryPlugin plugin =
  44. {
  45. MLHDR_VER,
  46. "nullsoft(ml_wire.dll)",
  47. Init,
  48. Quit,
  49. MessageProc,
  50. 0,
  51. 0,
  52. 0,
  53. };
  54. static prefsDlgRecW preferences;
  55. static wchar_t preferencesName[64] = {0};
  56. void SaveChannels(ChannelList &channels)
  57. {
  58. // generate a backup of the feeds.xml to cope with it being wiped randomly for some
  59. // people or it not being able to be saved in time e.g. during a forced OS shutdown
  60. CopyFile(feedsXmlFileName, feedsXmlFileNameBackup, FALSE);
  61. SaveChannels(feedsXmlFileName, channels);
  62. }
  63. void SaveAll(bool rss_only)
  64. {
  65. if (AGAVE_API_STATS)
  66. AGAVE_API_STATS->SetStat(api_stats::PODCAST_COUNT, (int)channels.size());
  67. if(!rss_only)
  68. SaveChannels(channels);
  69. // generate a backup of the rss.xml to cope with it being wiped randomly for some
  70. // people or it not being able to be saved in time e.g. during a forced OS shutdown
  71. CopyFile(rssXmlFileName, rssXmlFileNameBackup, FALSE);
  72. SaveSettings(rssXmlFileName, downloadedFiles);
  73. }
  74. static PodcastsFactory podcastsFactory;
  75. HANDLE hMainThread = NULL;
  76. HCURSOR hDragNDropCursor = NULL;
  77. int winampVersion = 0;
  78. JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = 0;
  79. JSAPI2Factory jsapi2Creator;
  80. obj_ombrowser *browserManager = NULL;
  81. api_application *applicationApi = NULL;
  82. api_stats *AGAVE_API_STATS = 0;
  83. api_threadpool *WASABI_API_THREADPOOL = 0;
  84. api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = 0;
  85. // wasabi based services for localisation support
  86. api_language *WASABI_API_LNG = 0;
  87. HINSTANCE WASABI_API_LNG_HINST = 0;
  88. HINSTANCE WASABI_API_ORIG_HINST = 0;
  89. static PCastFactory pcastFactory;
  90. static void CALLBACK InitTimer(HWND hwnd, UINT uMsg, UINT_PTR eventId, ULONG elapsed)
  91. {
  92. KillTimer(hwnd, eventId);
  93. {
  94. Nullsoft::Utility::AutoLock lock (channels LOCKNAME("feeds.xml load!"));
  95. FeedParse downloader(&channelMgr, true);
  96. downloader.DownloadFile(feedsXmlFileName);
  97. if (AGAVE_API_STATS)
  98. AGAVE_API_STATS->SetStat(api_stats::PODCAST_COUNT, (int)channels.size());
  99. }
  100. if (updateOnLaunch)
  101. {
  102. cloud.RefreshAll();
  103. }
  104. cloud.Init();
  105. cloud.Pulse();
  106. }
  107. int Init()
  108. {
  109. hMainThread = GetCurrentThread();
  110. hDragNDropCursor = LoadCursor( GetModuleHandle( L"gen_ml.dll" ), MAKEINTRESOURCE( ML_IDC_DRAGDROP ) );
  111. threadStorage = TlsAlloc();
  112. if ( 0 == VIEWPROP )
  113. {
  114. VIEWPROP = GlobalAddAtom( L"Nullsoft_PodcastView" );
  115. if ( VIEWPROP == 0 )
  116. return 1;
  117. }
  118. winampVersion = SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETVERSION );
  119. ml_cfg = (wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW);
  120. plugin.service->service_register( &podcastsFactory );
  121. plugin.service->service_register( &jsapi2Creator );
  122. plugin.service->service_register( &pcastFactory );
  123. // loader so that we can get the localisation service api for use
  124. waServiceFactory *sf = plugin.service->service_getServiceByGuid( languageApiGUID );
  125. if ( sf )
  126. WASABI_API_LNG = reinterpret_cast<api_language*>( sf->getInterface() );
  127. sf = plugin.service->service_getServiceByGuid( JSAPI2::api_securityGUID );
  128. if ( sf )
  129. AGAVE_API_JSAPI2_SECURITY = reinterpret_cast<JSAPI2::api_security*>( sf->getInterface() );
  130. sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid );
  131. if ( sf )
  132. WASABI_API_APP = reinterpret_cast<api_application*>( sf->getInterface() );
  133. sf = plugin.service->service_getServiceByGuid( OBJ_OmBrowser );
  134. if ( sf )
  135. OMBROWSERMNGR = reinterpret_cast<obj_ombrowser*>( sf->getInterface() );
  136. sf = plugin.service->service_getServiceByGuid( AnonymousStatsGUID );
  137. if ( sf )
  138. AGAVE_API_STATS = reinterpret_cast<api_stats*>( sf->getInterface() );
  139. sf = plugin.service->service_getServiceByGuid( ThreadPoolGUID );
  140. if ( sf )
  141. WASABI_API_THREADPOOL = reinterpret_cast<api_threadpool*>( sf->getInterface() );
  142. sf = plugin.service->service_getServiceByGuid( DownloadManagerGUID );
  143. if ( sf )
  144. WAC_API_DOWNLOADMANAGER = reinterpret_cast<api_downloadManager*>( sf->getInterface() );
  145. sf = plugin.service->service_getServiceByGuid( ExplorerFindFileApiGUID );
  146. if ( sf )
  147. WASABI_API_EXPLORERFINDFILE = reinterpret_cast<api_explorerfindfile*>( sf->getInterface() );
  148. // need to have this initialised before we try to do anything with localisation features
  149. WASABI_API_START_LANG( plugin.hDllInstance, MlWireLangGUID );
  150. static wchar_t szDescription[ 256 ];
  151. StringCchPrintfW( szDescription, ARRAYSIZE( szDescription ), WASABI_API_LNGSTRINGW( IDS_PLUGIN_NAME ), PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR );
  152. plugin.description = (char*)szDescription;
  153. mediaLibrary.library = plugin.hwndLibraryParent;
  154. mediaLibrary.winamp = plugin.hwndWinampParent;
  155. mediaLibrary.instance = plugin.hDllInstance;
  156. RssCOM *rss;
  157. if (SUCCEEDED(RssCOM::CreateInstance(&rss)))
  158. {
  159. DispatchInfo dispatchInfo;
  160. dispatchInfo.name = (LPWSTR)rss->GetName();
  161. dispatchInfo.dispatch = rss;
  162. SENDWAIPC(plugin.hwndWinampParent, IPC_ADD_DISPATCH_OBJECT, (WPARAM)&dispatchInfo);
  163. rss->Release();
  164. }
  165. BuildDefaultDownloadPath( plugin.hwndWinampParent );
  166. preferences.hInst = WASABI_API_LNG_HINST;
  167. preferences.dlgID = IDD_PREFERENCES;
  168. preferences.proc = (void *)PreferencesDialogProc;
  169. preferences.name = WASABI_API_LNGSTRINGW_BUF( IDS_PODCAST_DIRECTORY, preferencesName, 64 );
  170. preferences.where = 6;
  171. mediaLibrary.AddPreferences( preferences );
  172. wchar_t g_path[MAX_PATH] = {0};
  173. mediaLibrary.BuildPath( L"Plugins\\ml\\feeds", g_path, MAX_PATH );
  174. CreateDirectoryW( g_path, NULL );
  175. wchar_t oldxmlFileName[1024] = {0}, oldxmlFileNameBackup[ 1024 ] = { 0 };
  176. mediaLibrary.BuildPath( L"Plugins\\ml\\rss.xml", oldxmlFileName, 1024 );
  177. mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\rss.xml", rssXmlFileName, 1024 );
  178. mediaLibrary.BuildPath( L"Plugins\\ml\\rss.xml.backup", oldxmlFileNameBackup, 1024 );
  179. mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\rss.xml.backup", rssXmlFileNameBackup, 1024 );
  180. if ( PathFileExists( oldxmlFileName ) && !PathFileExists(rssXmlFileName))
  181. {
  182. MoveFile( oldxmlFileName, rssXmlFileName );
  183. MoveFile( oldxmlFileNameBackup, rssXmlFileNameBackup );
  184. }
  185. {
  186. DownloadsParse downloader;
  187. downloader.DownloadFile(rssXmlFileName);
  188. }
  189. mediaLibrary.BuildPath( L"Plugins\\ml\\feeds.xml", oldxmlFileName, 1024 );
  190. mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\feeds.xml", feedsXmlFileName, 1024 );
  191. mediaLibrary.BuildPath( L"Plugins\\ml\\feeds.xml.backup", oldxmlFileNameBackup, 1024 );
  192. mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\feeds.xml.backup", feedsXmlFileNameBackup, 1024 );
  193. if ( PathFileExists( oldxmlFileName ) && !PathFileExists( feedsXmlFileName ) )
  194. {
  195. MoveFile( oldxmlFileName, feedsXmlFileName );
  196. MoveFile( oldxmlFileNameBackup, feedsXmlFileNameBackup );
  197. }
  198. Navigation_Initialize();
  199. SetTimer( plugin.hwndLibraryParent, 0x498, 10, InitTimer );
  200. return 0;
  201. }
  202. void Quit()
  203. {
  204. // If there are still files downloading, cancel download to remove incomplete downloaded files
  205. while ( downloadStatus.CurrentlyDownloading() )
  206. {
  207. Nullsoft::Utility::AutoLock lock( downloadStatus.statusLock );
  208. DownloadToken dltoken = downloadStatus.downloads.begin()->first;
  209. WAC_API_DOWNLOADMANAGER->CancelDownload( dltoken );
  210. }
  211. cloud.Quit();
  212. CloseDatabase();
  213. plugin.service->service_deregister( &podcastsFactory );
  214. plugin.service->service_deregister( &jsapi2Creator );
  215. waServiceFactory *sf = plugin.service->service_getServiceByGuid( OBJ_OmBrowser );
  216. if ( sf != NULL )
  217. sf->releaseInterface( OMBROWSERMNGR );
  218. sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid );
  219. if ( sf != NULL )
  220. sf->releaseInterface(WASABI_API_APP);
  221. sf = plugin.service->service_getServiceByGuid(AnonymousStatsGUID);
  222. if ( sf != NULL )
  223. sf->releaseInterface(AGAVE_API_STATS);
  224. sf = plugin.service->service_getServiceByGuid(ThreadPoolGUID);
  225. if ( sf != NULL )
  226. sf->releaseInterface(WASABI_API_THREADPOOL);
  227. sf = plugin.service->service_getServiceByGuid(ExplorerFindFileApiGUID);
  228. if ( sf != NULL )
  229. sf->releaseInterface(WASABI_API_EXPLORERFINDFILE);
  230. sf = plugin.service->service_getServiceByGuid(DownloadManagerGUID);
  231. if ( sf != NULL )
  232. sf->releaseInterface(WAC_API_DOWNLOADMANAGER);
  233. if ( VIEWPROP != 0 )
  234. {
  235. GlobalDeleteAtom(VIEWPROP);
  236. VIEWPROP = 0;
  237. }
  238. }
  239. static INT_PTR Podcast_OnContextMenu( INT_PTR param1, HWND hHost, POINTS pts)
  240. {
  241. HNAVITEM hItem = (HNAVITEM)param1;
  242. HNAVITEM myItem = Navigation_FindService( SERVICE_PODCAST, NULL, NULL);
  243. HNAVITEM podcastItem = MLNavItem_GetChild( plugin.hwndLibraryParent, myItem);
  244. HNAVITEM subscriptionItem = Navigation_FindService( SERVICE_SUBSCRIPTION, podcastItem, NULL);
  245. if ( hItem != myItem && hItem != subscriptionItem )
  246. return FALSE;
  247. POINT pt;
  248. POINTSTOPOINT( pt, pts );
  249. if ( pt.x == -1 || pt.y == -1 )
  250. {
  251. NAVITEMGETRECT itemRect;
  252. itemRect.fItem = FALSE;
  253. itemRect.hItem = hItem;
  254. if ( MLNavItem_GetRect( plugin.hwndLibraryParent, &itemRect ) )
  255. {
  256. MapWindowPoints( hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2 );
  257. pt.x = itemRect.rc.left + 2;
  258. pt.y = itemRect.rc.top + 2;
  259. }
  260. }
  261. HMENU hMenu = WASABI_API_LOADMENU( IDR_MENU1 );
  262. int subMenuId = ( hItem == subscriptionItem )? 4 : 3;
  263. HMENU subMenu = ( NULL != hMenu ) ? GetSubMenu( hMenu, subMenuId ) : NULL;
  264. if ( subMenu != NULL )
  265. {
  266. INT r = Menu_TrackPopup( plugin.hwndLibraryParent, subMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, pt.x, pt.y, hHost, NULL );
  267. switch(r)
  268. {
  269. case ID_NAVIGATION_DIRECTORY:
  270. MLNavItem_Select( plugin.hwndLibraryParent, myItem );
  271. break;
  272. case ID_NAVIGATION_PREFERENCES:
  273. SENDWAIPC( plugin.hwndWinampParent, IPC_OPENPREFSTOPAGE, &preferences );
  274. break;
  275. case ID_NAVIGATION_HELP:
  276. SENDWAIPC( plugin.hwndWinampParent, IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8112346487060-Podcast-Directory" );
  277. break;
  278. case ID_NAVIGATION_REFRESHALL:
  279. cloud.RefreshAll();
  280. cloud.Pulse();
  281. break;
  282. }
  283. }
  284. if ( hMenu != NULL )
  285. DestroyMenu( hMenu );
  286. return TRUE;
  287. }
  288. INT_PTR MessageProc( int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3 )
  289. {
  290. INT_PTR result = 0;
  291. if ( Navigation_ProcessMessage( msg, param1, param2, param3, &result ) != FALSE )
  292. return result;
  293. switch ( msg )
  294. {
  295. case ML_MSG_NOTOKTOQUIT:
  296. {
  297. if (downloadStatus.CurrentlyDownloading())
  298. {
  299. wchar_t titleStr[32] = {0};
  300. if ( MessageBox( plugin.hwndLibraryParent, WASABI_API_LNGSTRINGW( IDS_CANCEL_DOWNLOADS_AND_QUIT ), WASABI_API_LNGSTRINGW_BUF( IDS_CONFIRM_QUIT, titleStr, 32 ), MB_YESNO | MB_ICONQUESTION ) == IDNO )
  301. return TRUE;
  302. }
  303. return 0;
  304. }
  305. case ML_MSG_CONFIG:
  306. mediaLibrary.GoToPreferences(preferences._id);
  307. return TRUE;
  308. case ML_MSG_NAVIGATION_CONTEXTMENU:
  309. return Podcast_OnContextMenu( param1, (HWND)param2, MAKEPOINTS( param3 ) );
  310. case ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE:
  311. enqueuedef = param1;
  312. groupBtn = param2;
  313. PostMessage( current_window, WM_APP + 104, param1, param2 );
  314. return 0;
  315. }
  316. return FALSE;
  317. }
  318. #define TREE_IMAGE_LOCAL_PODCASTS 108
  319. void addToLibrary(const DownloadedFile& d)
  320. {
  321. itemRecordW item = { 0 };
  322. item.year = -1;
  323. item.track = -1;
  324. item.tracks = -1;
  325. item.length = -1;
  326. item.rating = -1;
  327. item.lastplay = -1;
  328. item.lastupd = -1;
  329. item.filetime = -1;
  330. item.filesize = -1;
  331. item.bitrate = -1;
  332. item.type = -1;
  333. item.disc = -1;
  334. item.discs = -1;
  335. item.bpm = -1;
  336. item.playcount = -1;
  337. item.filename = _wcsdup( d.path );
  338. setRecordExtendedItem(&item,L"ispodcast",L"1");
  339. setRecordExtendedItem(&item,L"podcastchannel",d.channel);
  340. wchar_t buf[40] = {0};
  341. _i64tow(d.publishDate,buf,10);
  342. if(d.publishDate)
  343. setRecordExtendedItem(&item,L"podcastpubdate",buf);
  344. LMDB_FILE_ADD_INFOW fai = {item.filename,-1,-1};
  345. SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&fai, ML_IPC_DB_ADDORUPDATEFILEW);
  346. SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&item, ML_IPC_DB_UPDATEITEMW);
  347. PostMessage(plugin.hwndLibraryParent, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
  348. freeRecord(&item);
  349. if(needToMakePodcastsView) {
  350. mlSmartViewInfo m = { sizeof( mlSmartViewInfo ),2,L"Podcasts", L"ispodcast = 1",461315,TREE_IMAGE_LOCAL_PODCASTS,0 };
  351. WASABI_API_LNGSTRINGW_BUF( IDS_PODCASTS, m.smartViewName, 128 );
  352. SendMessage( plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&m, ML_IPC_SMARTVIEW_ADD );
  353. needToMakePodcastsView = false;
  354. }
  355. }
  356. typedef struct
  357. {
  358. const DownloadedFile *d;
  359. volatile UINT done;
  360. } apc_addtolib_waiter;
  361. static VOID CALLBACK apc_addtolib(ULONG_PTR dwParam)
  362. {
  363. apc_addtolib_waiter *w = (apc_addtolib_waiter *)dwParam;
  364. addToLibrary(*w->d);
  365. w->done=1;
  366. }
  367. void addToLibrary_thread(const DownloadedFile& d)
  368. {
  369. apc_addtolib_waiter w = { &d, 0 };
  370. if ( !QueueUserAPC( apc_addtolib, hMainThread, (ULONG_PTR)&w ) )
  371. return;
  372. while ( !w.done )
  373. SleepEx( 5, true );
  374. }
  375. extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
  376. {
  377. return &plugin;
  378. }