mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-12-20 08:55:52 +01:00
469 lines
14 KiB
C++
469 lines
14 KiB
C++
#include "main.h"
|
|
#include "Cloud.h"
|
|
#include "DownloadThread.h"
|
|
#include "OPMLParse.h"
|
|
#include "FeedsDialog.h"
|
|
#include "DownloadsParse.h"
|
|
#include "XMLWriter.h"
|
|
#include "FeedParse.h"
|
|
#include "DownloadsDialog.h"
|
|
#include "Preferences.h"
|
|
#include "..\..\General\gen_ml/ml.h"
|
|
#include "Defaults.h"
|
|
#include "Wire.h"
|
|
#include "..\..\General\gen_ml/ml_ipc_0313.h"
|
|
#include "RSSCOM.h"
|
|
|
|
#include "api__ml_wire.h"
|
|
#include "Downloaded.h"
|
|
#include "DownloadStatus.h"
|
|
#include "Factory.h"
|
|
#include "JSAPI2_Creator.h"
|
|
#include "./navigation.h"
|
|
|
|
#include <strsafe.h>
|
|
|
|
#include "PCastFactory.h"
|
|
|
|
Cloud cloud;
|
|
WireManager channelMgr;
|
|
|
|
int treeId = 0, allId = 0
|
|
#if 0
|
|
, discoverId = 0
|
|
#endif
|
|
;
|
|
MLTREEITEMW downloadsTree;
|
|
wchar_t downloadsStr[64] = {0}, *ml_cfg = 0,
|
|
feedsXmlFileName[1024] = {0},
|
|
feedsXmlFileNameBackup[1024] = {0},
|
|
rssXmlFileName[1024] = {0},
|
|
rssXmlFileNameBackup[1024] = {0};
|
|
|
|
ATOM VIEWPROP = 0;
|
|
|
|
api_downloadManager *WAC_API_DOWNLOADMANAGER = 0;
|
|
|
|
static int Init();
|
|
static void Quit();
|
|
static INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3);
|
|
|
|
DWORD threadStorage=TLS_OUT_OF_INDEXES;
|
|
extern "C" winampMediaLibraryPlugin plugin =
|
|
{
|
|
MLHDR_VER,
|
|
"nullsoft(ml_wire.dll)",
|
|
Init,
|
|
Quit,
|
|
MessageProc,
|
|
0,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
static prefsDlgRecW preferences;
|
|
|
|
static wchar_t preferencesName[64] = {0};
|
|
void SaveChannels(ChannelList &channels)
|
|
{
|
|
// generate a backup of the feeds.xml to cope with it being wiped randomly for some
|
|
// people or it not being able to be saved in time e.g. during a forced OS shutdown
|
|
CopyFile(feedsXmlFileName, feedsXmlFileNameBackup, FALSE);
|
|
SaveChannels(feedsXmlFileName, channels);
|
|
}
|
|
|
|
void SaveAll(bool rss_only)
|
|
{
|
|
if (AGAVE_API_STATS)
|
|
AGAVE_API_STATS->SetStat(api_stats::PODCAST_COUNT, (int)channels.size());
|
|
|
|
if(!rss_only)
|
|
SaveChannels(channels);
|
|
|
|
// generate a backup of the rss.xml to cope with it being wiped randomly for some
|
|
// people or it not being able to be saved in time e.g. during a forced OS shutdown
|
|
CopyFile(rssXmlFileName, rssXmlFileNameBackup, FALSE);
|
|
SaveSettings(rssXmlFileName, downloadedFiles);
|
|
}
|
|
|
|
static PodcastsFactory podcastsFactory;
|
|
|
|
HANDLE hMainThread = NULL;
|
|
|
|
HCURSOR hDragNDropCursor = NULL;
|
|
int winampVersion = 0;
|
|
|
|
JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = 0;
|
|
JSAPI2Factory jsapi2Creator;
|
|
|
|
obj_ombrowser *browserManager = NULL;
|
|
api_application *applicationApi = NULL;
|
|
api_stats *AGAVE_API_STATS = 0;
|
|
api_threadpool *WASABI_API_THREADPOOL = 0;
|
|
api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = 0;
|
|
|
|
// wasabi based services for localisation support
|
|
api_language *WASABI_API_LNG = 0;
|
|
HINSTANCE WASABI_API_LNG_HINST = 0;
|
|
HINSTANCE WASABI_API_ORIG_HINST = 0;
|
|
|
|
static PCastFactory pcastFactory;
|
|
|
|
static void CALLBACK InitTimer(HWND hwnd, UINT uMsg, UINT_PTR eventId, ULONG elapsed)
|
|
{
|
|
KillTimer(hwnd, eventId);
|
|
|
|
{
|
|
Nullsoft::Utility::AutoLock lock (channels LOCKNAME("feeds.xml load!"));
|
|
|
|
FeedParse downloader(&channelMgr, true);
|
|
downloader.DownloadFile(feedsXmlFileName);
|
|
if (AGAVE_API_STATS)
|
|
AGAVE_API_STATS->SetStat(api_stats::PODCAST_COUNT, (int)channels.size());
|
|
}
|
|
|
|
if (updateOnLaunch)
|
|
{
|
|
cloud.RefreshAll();
|
|
}
|
|
|
|
cloud.Init();
|
|
cloud.Pulse();
|
|
}
|
|
|
|
int Init()
|
|
{
|
|
hMainThread = GetCurrentThread();
|
|
hDragNDropCursor = LoadCursor( GetModuleHandle( L"gen_ml.dll" ), MAKEINTRESOURCE( ML_IDC_DRAGDROP ) );
|
|
threadStorage = TlsAlloc();
|
|
|
|
if ( 0 == VIEWPROP )
|
|
{
|
|
VIEWPROP = GlobalAddAtom( L"Nullsoft_PodcastView" );
|
|
if ( VIEWPROP == 0 )
|
|
return 1;
|
|
}
|
|
|
|
winampVersion = SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETVERSION );
|
|
ml_cfg = (wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW);
|
|
|
|
plugin.service->service_register( &podcastsFactory );
|
|
plugin.service->service_register( &jsapi2Creator );
|
|
plugin.service->service_register( &pcastFactory );
|
|
|
|
// loader so that we can get the localisation service api for use
|
|
waServiceFactory *sf = plugin.service->service_getServiceByGuid( languageApiGUID );
|
|
if ( sf )
|
|
WASABI_API_LNG = reinterpret_cast<api_language*>( sf->getInterface() );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( JSAPI2::api_securityGUID );
|
|
if ( sf )
|
|
AGAVE_API_JSAPI2_SECURITY = reinterpret_cast<JSAPI2::api_security*>( sf->getInterface() );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid );
|
|
if ( sf )
|
|
WASABI_API_APP = reinterpret_cast<api_application*>( sf->getInterface() );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( OBJ_OmBrowser );
|
|
if ( sf )
|
|
OMBROWSERMNGR = reinterpret_cast<obj_ombrowser*>( sf->getInterface() );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( AnonymousStatsGUID );
|
|
if ( sf )
|
|
AGAVE_API_STATS = reinterpret_cast<api_stats*>( sf->getInterface() );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( ThreadPoolGUID );
|
|
if ( sf )
|
|
WASABI_API_THREADPOOL = reinterpret_cast<api_threadpool*>( sf->getInterface() );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( DownloadManagerGUID );
|
|
if ( sf )
|
|
WAC_API_DOWNLOADMANAGER = reinterpret_cast<api_downloadManager*>( sf->getInterface() );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( ExplorerFindFileApiGUID );
|
|
if ( sf )
|
|
WASABI_API_EXPLORERFINDFILE = reinterpret_cast<api_explorerfindfile*>( sf->getInterface() );
|
|
|
|
// need to have this initialised before we try to do anything with localisation features
|
|
WASABI_API_START_LANG( plugin.hDllInstance, MlWireLangGUID );
|
|
|
|
static wchar_t szDescription[ 256 ];
|
|
StringCchPrintfW( 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;
|
|
|
|
RssCOM *rss;
|
|
if (SUCCEEDED(RssCOM::CreateInstance(&rss)))
|
|
{
|
|
DispatchInfo dispatchInfo;
|
|
dispatchInfo.name = (LPWSTR)rss->GetName();
|
|
dispatchInfo.dispatch = rss;
|
|
|
|
SENDWAIPC(plugin.hwndWinampParent, IPC_ADD_DISPATCH_OBJECT, (WPARAM)&dispatchInfo);
|
|
rss->Release();
|
|
}
|
|
|
|
BuildDefaultDownloadPath( plugin.hwndWinampParent );
|
|
|
|
preferences.hInst = WASABI_API_LNG_HINST;
|
|
preferences.dlgID = IDD_PREFERENCES;
|
|
preferences.proc = (void *)PreferencesDialogProc;
|
|
preferences.name = WASABI_API_LNGSTRINGW_BUF( IDS_PODCAST_DIRECTORY, preferencesName, 64 );
|
|
preferences.where = 6;
|
|
|
|
mediaLibrary.AddPreferences( preferences );
|
|
|
|
wchar_t g_path[MAX_PATH] = {0};
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\feeds", g_path, MAX_PATH );
|
|
CreateDirectoryW( g_path, NULL );
|
|
|
|
wchar_t oldxmlFileName[1024] = {0}, oldxmlFileNameBackup[ 1024 ] = { 0 };
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\rss.xml", oldxmlFileName, 1024 );
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\rss.xml", rssXmlFileName, 1024 );
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\rss.xml.backup", oldxmlFileNameBackup, 1024 );
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\rss.xml.backup", rssXmlFileNameBackup, 1024 );
|
|
|
|
if ( PathFileExists( oldxmlFileName ) && !PathFileExists(rssXmlFileName))
|
|
{
|
|
MoveFile( oldxmlFileName, rssXmlFileName );
|
|
MoveFile( oldxmlFileNameBackup, rssXmlFileNameBackup );
|
|
}
|
|
|
|
{
|
|
DownloadsParse downloader;
|
|
downloader.DownloadFile(rssXmlFileName);
|
|
}
|
|
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\feeds.xml", oldxmlFileName, 1024 );
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\feeds.xml", feedsXmlFileName, 1024 );
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\feeds.xml.backup", oldxmlFileNameBackup, 1024 );
|
|
mediaLibrary.BuildPath( L"Plugins\\ml\\feeds\\feeds.xml.backup", feedsXmlFileNameBackup, 1024 );
|
|
|
|
if ( PathFileExists( oldxmlFileName ) && !PathFileExists( feedsXmlFileName ) )
|
|
{
|
|
MoveFile( oldxmlFileName, feedsXmlFileName );
|
|
MoveFile( oldxmlFileNameBackup, feedsXmlFileNameBackup );
|
|
}
|
|
|
|
Navigation_Initialize();
|
|
SetTimer( plugin.hwndLibraryParent, 0x498, 10, InitTimer );
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Quit()
|
|
{
|
|
// If there are still files downloading, cancel download to remove incomplete downloaded files
|
|
while ( downloadStatus.CurrentlyDownloading() )
|
|
{
|
|
Nullsoft::Utility::AutoLock lock( downloadStatus.statusLock );
|
|
DownloadToken dltoken = downloadStatus.downloads.begin()->first;
|
|
WAC_API_DOWNLOADMANAGER->CancelDownload( dltoken );
|
|
}
|
|
|
|
cloud.Quit();
|
|
CloseDatabase();
|
|
|
|
plugin.service->service_deregister( &podcastsFactory );
|
|
plugin.service->service_deregister( &jsapi2Creator );
|
|
|
|
waServiceFactory *sf = plugin.service->service_getServiceByGuid( OBJ_OmBrowser );
|
|
if ( sf != NULL )
|
|
sf->releaseInterface( OMBROWSERMNGR );
|
|
|
|
sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid );
|
|
if ( sf != NULL )
|
|
sf->releaseInterface(WASABI_API_APP);
|
|
|
|
sf = plugin.service->service_getServiceByGuid(AnonymousStatsGUID);
|
|
if ( sf != NULL )
|
|
sf->releaseInterface(AGAVE_API_STATS);
|
|
|
|
sf = plugin.service->service_getServiceByGuid(ThreadPoolGUID);
|
|
if ( sf != NULL )
|
|
sf->releaseInterface(WASABI_API_THREADPOOL);
|
|
|
|
sf = plugin.service->service_getServiceByGuid(ExplorerFindFileApiGUID);
|
|
if ( sf != NULL )
|
|
sf->releaseInterface(WASABI_API_EXPLORERFINDFILE);
|
|
|
|
sf = plugin.service->service_getServiceByGuid(DownloadManagerGUID);
|
|
if ( sf != NULL )
|
|
sf->releaseInterface(WAC_API_DOWNLOADMANAGER);
|
|
|
|
if ( VIEWPROP != 0 )
|
|
{
|
|
GlobalDeleteAtom(VIEWPROP);
|
|
VIEWPROP = 0;
|
|
}
|
|
}
|
|
|
|
static INT_PTR Podcast_OnContextMenu( INT_PTR param1, HWND hHost, POINTS pts)
|
|
{
|
|
HNAVITEM hItem = (HNAVITEM)param1;
|
|
HNAVITEM myItem = Navigation_FindService( SERVICE_PODCAST, NULL, NULL);
|
|
|
|
HNAVITEM podcastItem = MLNavItem_GetChild( plugin.hwndLibraryParent, myItem);
|
|
HNAVITEM subscriptionItem = Navigation_FindService( SERVICE_SUBSCRIPTION, podcastItem, NULL);
|
|
|
|
if ( hItem != myItem && hItem != subscriptionItem )
|
|
return FALSE;
|
|
|
|
POINT pt;
|
|
POINTSTOPOINT( pt, pts );
|
|
if ( pt.x == -1 || pt.y == -1 )
|
|
{
|
|
NAVITEMGETRECT itemRect;
|
|
itemRect.fItem = FALSE;
|
|
itemRect.hItem = hItem;
|
|
if ( MLNavItem_GetRect( plugin.hwndLibraryParent, &itemRect ) )
|
|
{
|
|
MapWindowPoints( hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2 );
|
|
pt.x = itemRect.rc.left + 2;
|
|
pt.y = itemRect.rc.top + 2;
|
|
}
|
|
}
|
|
|
|
HMENU hMenu = WASABI_API_LOADMENU( IDR_MENU1 );
|
|
int subMenuId = ( hItem == subscriptionItem )? 4 : 3;
|
|
|
|
HMENU subMenu = ( NULL != hMenu ) ? GetSubMenu( hMenu, subMenuId ) : NULL;
|
|
if ( subMenu != NULL )
|
|
{
|
|
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 );
|
|
|
|
switch(r)
|
|
{
|
|
case ID_NAVIGATION_DIRECTORY:
|
|
MLNavItem_Select( plugin.hwndLibraryParent, myItem );
|
|
break;
|
|
case ID_NAVIGATION_PREFERENCES:
|
|
SENDWAIPC( plugin.hwndWinampParent, IPC_OPENPREFSTOPAGE, &preferences );
|
|
break;
|
|
case ID_NAVIGATION_HELP:
|
|
SENDWAIPC( plugin.hwndWinampParent, IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8112346487060-Podcast-Directory" );
|
|
break;
|
|
case ID_NAVIGATION_REFRESHALL:
|
|
cloud.RefreshAll();
|
|
cloud.Pulse();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( hMenu != NULL )
|
|
DestroyMenu( hMenu );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR MessageProc( int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3 )
|
|
{
|
|
INT_PTR result = 0;
|
|
if ( Navigation_ProcessMessage( msg, param1, param2, param3, &result ) != FALSE )
|
|
return result;
|
|
|
|
switch ( msg )
|
|
{
|
|
case ML_MSG_NOTOKTOQUIT:
|
|
{
|
|
if (downloadStatus.CurrentlyDownloading())
|
|
{
|
|
wchar_t titleStr[32] = {0};
|
|
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 )
|
|
return TRUE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
case ML_MSG_CONFIG:
|
|
mediaLibrary.GoToPreferences(preferences._id);
|
|
return TRUE;
|
|
case ML_MSG_NAVIGATION_CONTEXTMENU:
|
|
return Podcast_OnContextMenu( param1, (HWND)param2, MAKEPOINTS( param3 ) );
|
|
case ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE:
|
|
enqueuedef = param1;
|
|
groupBtn = param2;
|
|
PostMessage( current_window, WM_APP + 104, param1, param2 );
|
|
return 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#define TREE_IMAGE_LOCAL_PODCASTS 108
|
|
|
|
void addToLibrary(const DownloadedFile& d)
|
|
{
|
|
itemRecordW item = { 0 };
|
|
|
|
item.year = -1;
|
|
item.track = -1;
|
|
item.tracks = -1;
|
|
item.length = -1;
|
|
item.rating = -1;
|
|
item.lastplay = -1;
|
|
item.lastupd = -1;
|
|
item.filetime = -1;
|
|
item.filesize = -1;
|
|
item.bitrate = -1;
|
|
item.type = -1;
|
|
item.disc = -1;
|
|
item.discs = -1;
|
|
item.bpm = -1;
|
|
item.playcount = -1;
|
|
item.filename = _wcsdup( d.path );
|
|
|
|
setRecordExtendedItem(&item,L"ispodcast",L"1");
|
|
setRecordExtendedItem(&item,L"podcastchannel",d.channel);
|
|
|
|
wchar_t buf[40] = {0};
|
|
_i64tow(d.publishDate,buf,10);
|
|
if(d.publishDate)
|
|
setRecordExtendedItem(&item,L"podcastpubdate",buf);
|
|
|
|
LMDB_FILE_ADD_INFOW fai = {item.filename,-1,-1};
|
|
SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&fai, ML_IPC_DB_ADDORUPDATEFILEW);
|
|
SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&item, ML_IPC_DB_UPDATEITEMW);
|
|
PostMessage(plugin.hwndLibraryParent, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
|
|
freeRecord(&item);
|
|
|
|
if(needToMakePodcastsView) {
|
|
mlSmartViewInfo m = { sizeof( mlSmartViewInfo ),2,L"Podcasts", L"ispodcast = 1",461315,TREE_IMAGE_LOCAL_PODCASTS,0 };
|
|
WASABI_API_LNGSTRINGW_BUF( IDS_PODCASTS, m.smartViewName, 128 );
|
|
SendMessage( plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&m, ML_IPC_SMARTVIEW_ADD );
|
|
needToMakePodcastsView = false;
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
const DownloadedFile *d;
|
|
volatile UINT done;
|
|
} apc_addtolib_waiter;
|
|
|
|
static VOID CALLBACK apc_addtolib(ULONG_PTR dwParam)
|
|
{
|
|
apc_addtolib_waiter *w = (apc_addtolib_waiter *)dwParam;
|
|
addToLibrary(*w->d);
|
|
w->done=1;
|
|
}
|
|
|
|
void addToLibrary_thread(const DownloadedFile& d)
|
|
{
|
|
apc_addtolib_waiter w = { &d, 0 };
|
|
if ( !QueueUserAPC( apc_addtolib, hMainThread, (ULONG_PTR)&w ) )
|
|
return;
|
|
|
|
while ( !w.done )
|
|
SleepEx( 5, true );
|
|
}
|
|
|
|
|
|
extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
|
|
{
|
|
return &plugin;
|
|
} |