diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_wire/BackgroundDownloader.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/Plugins/Library/ml_wire/BackgroundDownloader.cpp')
-rw-r--r-- | Src/Plugins/Library/ml_wire/BackgroundDownloader.cpp | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_wire/BackgroundDownloader.cpp b/Src/Plugins/Library/ml_wire/BackgroundDownloader.cpp new file mode 100644 index 00000000..9e8c0cac --- /dev/null +++ b/Src/Plugins/Library/ml_wire/BackgroundDownloader.cpp @@ -0,0 +1,338 @@ +#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
\ No newline at end of file |