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 2
- std::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 DownloaderCallback
- START_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
|