| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 | #include <vector>#include <atomic>#include "Main.h"#include "Downloaded.h"#include "BackgroundDownloader.h"#include "Feeds.h"#include "DownloadStatus.h"#include "DownloadsDialog.h"#include "api__ml_wire.h"#include "api/service/waServiceFactory.h"#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"using namespace Nullsoft::Utility;#define SIMULTANEOUS_DOWNLOADS 2std::vector<DownloadToken> downloadsQueue;LockGuard downloadsQueueLock;class DownloaderCallback : public ifc_downloadManagerCallback{public:	DownloaderCallback( const wchar_t *url, const wchar_t *destination_filepath, const wchar_t *channel, const wchar_t *item, __time64_t publishDate )	{		this->hFile                = INVALID_HANDLE_VALUE;		this->url                  = _wcsdup( url );		this->destination_filepath = _wcsdup( destination_filepath );		this->channel              = _wcsdup( channel );		this->item                 = _wcsdup( item );		this->publishDate          = publishDate;		this->totalSize            = 0;		this->downloaded           = 0;	}		void OnInit(DownloadToken token)	{		// ---- Inform the download status service of our presence----		downloadStatus.AddDownloadThread(token, this->channel, this->item, this->destination_filepath);	}	void OnConnect(DownloadToken token)	{		// ---- retrieve total size		api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token);		if (http) 		{			this->totalSize = http->content_length();		}		// ---- create file handle		hFile = CreateFile(destination_filepath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);		if ( hFile == INVALID_HANDLE_VALUE ) 		{			WAC_API_DOWNLOADMANAGER->CancelDownload(token);		}			}	void OnData(DownloadToken token, void *data, size_t datalen)	{		// ---- OnConnect copied here due to dlmgr OnData called first		// ---- retrieve total size		api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token);		if ( !this->totalSize && http ) 		{			this->totalSize = http->content_length();		}		if ( hFile == INVALID_HANDLE_VALUE )		{			// ---- create file handle			hFile = CreateFile(destination_filepath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);			if ( hFile == INVALID_HANDLE_VALUE ) 			{				WAC_API_DOWNLOADMANAGER->CancelDownload(token);				return;			}				}		// ---- OnConnect to be removed once dlmgr is fixed		// ---- OnData		// ---- if file handle is invalid, then cancel download		if ( hFile == INVALID_HANDLE_VALUE )		{			WAC_API_DOWNLOADMANAGER->CancelDownload(token);			return;		}		this->downloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded(token);		if ( datalen > 0 ) 		{			// ---- hFile is valid handle, and write to disk			DWORD numWritten = 0;			WriteFile(hFile, data,(DWORD)datalen, &numWritten, FALSE);						// ---- failed writing the number of datalen characters, cancel download			if (numWritten != datalen) 			{				WAC_API_DOWNLOADMANAGER->CancelDownload(token);				return;			}		}			// if killswitch is turned on, then cancel download		if ( downloadStatus.UpdateStatus(token, this->downloaded, this->totalSize) )		{			WAC_API_DOWNLOADMANAGER->CancelDownload(token);		}	}	void OnCancel(DownloadToken token)	{		if ( hFile != INVALID_HANDLE_VALUE ) 		{			CloseHandle(hFile);			DeleteFile(destination_filepath);		}		DownloadsUpdated(token,NULL);		downloadStatus.DownloadThreadDone(token);		{			AutoLock lock( downloadsQueueLock );			size_t l_index = 0;			for ( DownloadToken &l_download_token : downloadsQueue )			{				if ( l_download_token == token )				{					downloadsQueue.erase( downloadsQueue.begin() + l_index );					break;				}				++l_index;			}		}		for ( DownloadToken &l_download_token : downloadsQueue )		{			if ( WAC_API_DOWNLOADMANAGER->IsPending( l_download_token ) )			{				WAC_API_DOWNLOADMANAGER->ResumePendingDownload( l_download_token );				break;			}		}		this->Release();	}		void OnError(DownloadToken token, int error)	{		if ( hFile != INVALID_HANDLE_VALUE ) 		{			CloseHandle(hFile);			DeleteFile(destination_filepath);		}		DownloadsUpdated(token,NULL);		downloadStatus.DownloadThreadDone(token);		{			AutoLock lock(downloadsQueueLock);			for (size_t index = 0; index < downloadsQueue.size(); index++)			{				if (downloadsQueue.at(index) == token)				{					downloadsQueue.erase(downloadsQueue.begin() + index);					break;				}			}			for (size_t index = 0; index < downloadsQueue.size(); index++)			{				if(WAC_API_DOWNLOADMANAGER->IsPending(downloadsQueue.at(index)))				{					WAC_API_DOWNLOADMANAGER->ResumePendingDownload(downloadsQueue.at(index));					break;				}			}		}		this->Release();	}	void OnFinish( DownloadToken token )	{		if ( hFile != INVALID_HANDLE_VALUE )		{			CloseHandle( hFile );			DownloadedFile *data = new DownloadedFile( this->url, this->destination_filepath, this->channel, this->item, this->publishDate );			data->bytesDownloaded = this->downloaded;			data->totalSize = this->totalSize;			{				AutoLock lock( downloadedFiles );				downloadedFiles.downloadList.push_back( *data );				addToLibrary_thread( *data );				AddPodcastData( *data );				DownloadsUpdated( token, &downloadedFiles.downloadList[ downloadedFiles.downloadList.size() - 1 ] );			}			downloadStatus.DownloadThreadDone( token );			delete data;		}		else		{			DownloadsUpdated( token, NULL );			downloadStatus.DownloadThreadDone( token );		}		{			AutoLock lock( downloadsQueueLock );			size_t l_index = 0;			for ( DownloadToken &l_download_token : downloadsQueue )			{				if ( l_download_token == token )				{					downloadsQueue.erase( downloadsQueue.begin() + l_index );					break;				}				++l_index;			}			for ( DownloadToken &l_download_token : downloadsQueue )			{				if ( WAC_API_DOWNLOADMANAGER->IsPending( l_download_token ) )				{					WAC_API_DOWNLOADMANAGER->ResumePendingDownload( l_download_token );					break;				}			}		}		this->Release();	}	int GetSource( wchar_t *source, size_t source_cch )	{		return wcscpy_s( source, source_cch, this->channel );	}	int GetTitle( wchar_t *title, size_t title_cch )	{		return wcscpy_s( title, title_cch, this->item );	}	int GetLocation( wchar_t *location, size_t location_cch )	{		return wcscpy_s( location, location_cch, this->destination_filepath );	}	size_t AddRef()	{		return _ref_count.fetch_add( 1 );	}	size_t Release()	{		if ( _ref_count.load() == 0 )			return _ref_count.load();		LONG r = _ref_count.fetch_sub( 1 );		if ( r == 0 )			delete( this );		return r;	}private: // private destructor so no one accidentally calls delete directly on this reference counted object	~DownloaderCallback()	{		if ( url )			free( url );		if ( destination_filepath )			free( destination_filepath );		if ( channel )			free( channel );		if ( item )			free( item );	}protected:	RECVS_DISPATCH;private:	HANDLE     hFile;	wchar_t    *url;	wchar_t    *destination_filepath;	wchar_t    *channel;	wchar_t    *item;	__time64_t  publishDate;	size_t      totalSize;	size_t      downloaded;	std::atomic<std::size_t> _ref_count = 1;};void BackgroundDownloader::Download( const wchar_t *url, const wchar_t *savePath, const wchar_t *channel, const wchar_t *item, __time64_t publishDate ){	DownloaderCallback *callback = new DownloaderCallback( url, savePath, channel, item, publishDate );	{		Nullsoft::Utility::AutoLock lock( downloadsQueueLock );		if ( downloadsQueue.size() < SIMULTANEOUS_DOWNLOADS )		{			DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx( AutoChar( url ), callback, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_UI );			downloadsQueue.push_back( dt );		}		else		{			DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx( AutoChar( url ), callback, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_PENDING | api_downloadManager::DOWNLOADEX_UI );			downloadsQueue.push_back( dt );		}	}}BackgroundDownloader downloader;#define CBCLASS DownloaderCallbackSTART_DISPATCH;VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH,    OnFinish )VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL,    OnCancel )VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR,     OnError )VCB( IFC_DOWNLOADMANAGERCALLBACK_ONDATA,      OnData )VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCONNECT,   OnConnect )VCB( IFC_DOWNLOADMANAGERCALLBACK_ONINIT,      OnInit )CB(  IFC_DOWNLOADMANAGERCALLBACK_GETSOURCE,   GetSource )CB(  IFC_DOWNLOADMANAGERCALLBACK_GETTITLE,    GetTitle )CB(  IFC_DOWNLOADMANAGERCALLBACK_GETLOCATION, GetLocation )CB(  ADDREF, AddRef )CB(  RELEASE, Release )END_DISPATCH;#undef CBCLASS
 |