BackgroundDownloader.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #include <vector>
  2. #include <atomic>
  3. #include "Main.h"
  4. #include "Downloaded.h"
  5. #include "BackgroundDownloader.h"
  6. #include "Feeds.h"
  7. #include "DownloadStatus.h"
  8. #include "DownloadsDialog.h"
  9. #include "api__ml_wire.h"
  10. #include "api/service/waServiceFactory.h"
  11. #include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
  12. using namespace Nullsoft::Utility;
  13. #define SIMULTANEOUS_DOWNLOADS 2
  14. std::vector<DownloadToken> downloadsQueue;
  15. LockGuard downloadsQueueLock;
  16. class DownloaderCallback : public ifc_downloadManagerCallback
  17. {
  18. public:
  19. DownloaderCallback( const wchar_t *url, const wchar_t *destination_filepath, const wchar_t *channel, const wchar_t *item, __time64_t publishDate )
  20. {
  21. this->hFile = INVALID_HANDLE_VALUE;
  22. this->url = _wcsdup( url );
  23. this->destination_filepath = _wcsdup( destination_filepath );
  24. this->channel = _wcsdup( channel );
  25. this->item = _wcsdup( item );
  26. this->publishDate = publishDate;
  27. this->totalSize = 0;
  28. this->downloaded = 0;
  29. }
  30. void OnInit(DownloadToken token)
  31. {
  32. // ---- Inform the download status service of our presence----
  33. downloadStatus.AddDownloadThread(token, this->channel, this->item, this->destination_filepath);
  34. }
  35. void OnConnect(DownloadToken token)
  36. {
  37. // ---- retrieve total size
  38. api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
  39. if (http)
  40. {
  41. this->totalSize = http->content_length();
  42. }
  43. // ---- create file handle
  44. hFile = CreateFile(destination_filepath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  45. if ( hFile == INVALID_HANDLE_VALUE )
  46. {
  47. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  48. }
  49. }
  50. void OnData(DownloadToken token, void *data, size_t datalen)
  51. {
  52. // ---- OnConnect copied here due to dlmgr OnData called first
  53. // ---- retrieve total size
  54. api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
  55. if ( !this->totalSize && http )
  56. {
  57. this->totalSize = http->content_length();
  58. }
  59. if ( hFile == INVALID_HANDLE_VALUE )
  60. {
  61. // ---- create file handle
  62. hFile = CreateFile(destination_filepath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  63. if ( hFile == INVALID_HANDLE_VALUE )
  64. {
  65. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  66. return;
  67. }
  68. }
  69. // ---- OnConnect to be removed once dlmgr is fixed
  70. // ---- OnData
  71. // ---- if file handle is invalid, then cancel download
  72. if ( hFile == INVALID_HANDLE_VALUE )
  73. {
  74. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  75. return;
  76. }
  77. this->downloaded = (size_t)WAC_API_DOWNLOADMANAGER->GetBytesDownloaded(token);
  78. if ( datalen > 0 )
  79. {
  80. // ---- hFile is valid handle, and write to disk
  81. DWORD numWritten = 0;
  82. WriteFile(hFile, data,(DWORD)datalen, &numWritten, FALSE);
  83. // ---- failed writing the number of datalen characters, cancel download
  84. if (numWritten != datalen)
  85. {
  86. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  87. return;
  88. }
  89. }
  90. // if killswitch is turned on, then cancel download
  91. if ( downloadStatus.UpdateStatus(token, this->downloaded, this->totalSize) )
  92. {
  93. WAC_API_DOWNLOADMANAGER->CancelDownload(token);
  94. }
  95. }
  96. void OnCancel(DownloadToken token)
  97. {
  98. if ( hFile != INVALID_HANDLE_VALUE )
  99. {
  100. CloseHandle(hFile);
  101. DeleteFile(destination_filepath);
  102. }
  103. DownloadsUpdated(token,NULL);
  104. downloadStatus.DownloadThreadDone(token);
  105. {
  106. AutoLock lock( downloadsQueueLock );
  107. size_t l_index = 0;
  108. for ( DownloadToken &l_download_token : downloadsQueue )
  109. {
  110. if ( l_download_token == token )
  111. {
  112. downloadsQueue.erase( downloadsQueue.begin() + l_index );
  113. break;
  114. }
  115. ++l_index;
  116. }
  117. }
  118. for ( DownloadToken &l_download_token : downloadsQueue )
  119. {
  120. if ( WAC_API_DOWNLOADMANAGER->IsPending( l_download_token ) )
  121. {
  122. WAC_API_DOWNLOADMANAGER->ResumePendingDownload( l_download_token );
  123. break;
  124. }
  125. }
  126. this->Release();
  127. }
  128. void OnError(DownloadToken token, int error)
  129. {
  130. if ( hFile != INVALID_HANDLE_VALUE )
  131. {
  132. CloseHandle(hFile);
  133. DeleteFile(destination_filepath);
  134. }
  135. DownloadsUpdated(token,NULL);
  136. downloadStatus.DownloadThreadDone(token);
  137. {
  138. AutoLock lock(downloadsQueueLock);
  139. for (size_t index = 0; index < downloadsQueue.size(); index++)
  140. {
  141. if (downloadsQueue.at(index) == token)
  142. {
  143. downloadsQueue.erase(downloadsQueue.begin() + index);
  144. break;
  145. }
  146. }
  147. for (size_t index = 0; index < downloadsQueue.size(); index++)
  148. {
  149. if(WAC_API_DOWNLOADMANAGER->IsPending(downloadsQueue.at(index)))
  150. {
  151. WAC_API_DOWNLOADMANAGER->ResumePendingDownload(downloadsQueue.at(index));
  152. break;
  153. }
  154. }
  155. }
  156. this->Release();
  157. }
  158. void OnFinish( DownloadToken token )
  159. {
  160. if ( hFile != INVALID_HANDLE_VALUE )
  161. {
  162. CloseHandle( hFile );
  163. DownloadedFile *data = new DownloadedFile( this->url, this->destination_filepath, this->channel, this->item, this->publishDate );
  164. data->bytesDownloaded = this->downloaded;
  165. data->totalSize = this->totalSize;
  166. {
  167. AutoLock lock( downloadedFiles );
  168. downloadedFiles.downloadList.push_back( *data );
  169. addToLibrary_thread( *data );
  170. AddPodcastData( *data );
  171. DownloadsUpdated( token, &downloadedFiles.downloadList[ downloadedFiles.downloadList.size() - 1 ] );
  172. }
  173. downloadStatus.DownloadThreadDone( token );
  174. delete data;
  175. }
  176. else
  177. {
  178. DownloadsUpdated( token, NULL );
  179. downloadStatus.DownloadThreadDone( token );
  180. }
  181. {
  182. AutoLock lock( downloadsQueueLock );
  183. size_t l_index = 0;
  184. for ( DownloadToken &l_download_token : downloadsQueue )
  185. {
  186. if ( l_download_token == token )
  187. {
  188. downloadsQueue.erase( downloadsQueue.begin() + l_index );
  189. break;
  190. }
  191. ++l_index;
  192. }
  193. for ( DownloadToken &l_download_token : downloadsQueue )
  194. {
  195. if ( WAC_API_DOWNLOADMANAGER->IsPending( l_download_token ) )
  196. {
  197. WAC_API_DOWNLOADMANAGER->ResumePendingDownload( l_download_token );
  198. break;
  199. }
  200. }
  201. }
  202. this->Release();
  203. }
  204. int GetSource( wchar_t *source, size_t source_cch )
  205. {
  206. return wcscpy_s( source, source_cch, this->channel );
  207. }
  208. int GetTitle( wchar_t *title, size_t title_cch )
  209. {
  210. return wcscpy_s( title, title_cch, this->item );
  211. }
  212. int GetLocation( wchar_t *location, size_t location_cch )
  213. {
  214. return wcscpy_s( location, location_cch, this->destination_filepath );
  215. }
  216. size_t AddRef()
  217. {
  218. return _ref_count.fetch_add( 1 );
  219. }
  220. size_t Release()
  221. {
  222. if ( _ref_count.load() == 0 )
  223. return _ref_count.load();
  224. LONG r = _ref_count.fetch_sub( 1 );
  225. if ( r == 0 )
  226. delete( this );
  227. return r;
  228. }
  229. private: // private destructor so no one accidentally calls delete directly on this reference counted object
  230. ~DownloaderCallback()
  231. {
  232. if ( url )
  233. free( url );
  234. if ( destination_filepath )
  235. free( destination_filepath );
  236. if ( channel )
  237. free( channel );
  238. if ( item )
  239. free( item );
  240. }
  241. protected:
  242. RECVS_DISPATCH;
  243. private:
  244. HANDLE hFile;
  245. wchar_t *url;
  246. wchar_t *destination_filepath;
  247. wchar_t *channel;
  248. wchar_t *item;
  249. __time64_t publishDate;
  250. size_t totalSize;
  251. size_t downloaded;
  252. std::atomic<std::size_t> _ref_count = 1;
  253. };
  254. void BackgroundDownloader::Download( const wchar_t *url, const wchar_t *savePath, const wchar_t *channel, const wchar_t *item, __time64_t publishDate )
  255. {
  256. DownloaderCallback *callback = new DownloaderCallback( url, savePath, channel, item, publishDate );
  257. {
  258. Nullsoft::Utility::AutoLock lock( downloadsQueueLock );
  259. if ( downloadsQueue.size() < SIMULTANEOUS_DOWNLOADS )
  260. {
  261. DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx( AutoChar( url ), callback, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_UI );
  262. downloadsQueue.push_back( dt );
  263. }
  264. else
  265. {
  266. DownloadToken dt = WAC_API_DOWNLOADMANAGER->DownloadEx( AutoChar( url ), callback, api_downloadManager::DOWNLOADEX_CALLBACK | api_downloadManager::DOWNLOADEX_PENDING | api_downloadManager::DOWNLOADEX_UI );
  267. downloadsQueue.push_back( dt );
  268. }
  269. }
  270. }
  271. BackgroundDownloader downloader;
  272. #define CBCLASS DownloaderCallback
  273. START_DISPATCH;
  274. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish )
  275. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, OnCancel )
  276. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnError )
  277. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONDATA, OnData )
  278. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCONNECT, OnConnect )
  279. VCB( IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit )
  280. CB( IFC_DOWNLOADMANAGERCALLBACK_GETSOURCE, GetSource )
  281. CB( IFC_DOWNLOADMANAGERCALLBACK_GETTITLE, GetTitle )
  282. CB( IFC_DOWNLOADMANAGERCALLBACK_GETLOCATION, GetLocation )
  283. CB( ADDREF, AddRef )
  284. CB( RELEASE, Release )
  285. END_DISPATCH;
  286. #undef CBCLASS