summaryrefslogtreecommitdiff
path: root/src/media.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-02-26 10:24:09 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-02-26 10:24:09 +0200
commit91a6225d8508db01574d7da2c013cb30d6a87ec8 (patch)
treee3bd2c2f24a22c694c1c23aefd5fc531ae108723 /src/media.c
parent4708a6580e9af65cd15769e87487fdf4456f1e00 (diff)
DocumentWidget: Inline downloads
Diffstat (limited to 'src/media.c')
-rw-r--r--src/media.c93
1 files changed, 89 insertions, 4 deletions
diff --git a/src/media.c b/src/media.c
index 65454756..000214b2 100644
--- a/src/media.c
+++ b/src/media.c
@@ -27,10 +27,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
27#include "audio/player.h" 27#include "audio/player.h"
28#include "app.h" 28#include "app.h"
29 29
30#include <the_Foundation/file.h>
30#include <the_Foundation/ptrarray.h> 31#include <the_Foundation/ptrarray.h>
31#include <stb_image.h> 32#include <stb_image.h>
32#include <SDL_hints.h> 33#include <SDL_hints.h>
33#include <SDL_render.h> 34#include <SDL_render.h>
35#include <SDL_timer.h>
34 36
35iDeclareType(GmMediaProps) 37iDeclareType(GmMediaProps)
36 38
@@ -132,15 +134,63 @@ iDeclareType(GmDownload)
132 134
133struct Impl_GmDownload { 135struct Impl_GmDownload {
134 iGmMediaProps props; 136 iGmMediaProps props;
135 /* TODO: Speed statistics. */ 137 uint64_t numBytes;
138 iTime startTime;
139 uint32_t rateStartTime;
140 size_t rateNumBytes;
141 float currentRate;
142 iString * path;
143 iFile * file;
136}; 144};
137 145
146static iBool openFile_GmDownload_(iGmDownload *d) {
147 iAssert(!isEmpty_String(&d->props.url));
148 d->path = copy_String(downloadPathForUrl_App(&d->props.url, &d->props.mime));
149 d->file = new_File(d->path);
150 if (!open_File(d->file, writeOnly_FileMode)) {
151 return iFalse;
152 }
153 return iTrue;
154}
155
156static void closeFile_GmDownload_(iGmDownload *d) {
157 d->currentRate = (float) (d->numBytes / elapsedSeconds_Time(&d->startTime));
158 iReleasePtr(&d->file);
159}
160
138void init_GmDownload(iGmDownload *d) { 161void init_GmDownload(iGmDownload *d) {
139 init_GmMediaProps_(&d->props); 162 init_GmMediaProps_(&d->props);
163 initCurrent_Time(&d->startTime);
164 d->numBytes = 0;
165 d->rateStartTime = SDL_GetTicks();
166 d->rateNumBytes = 0;
167 d->currentRate = 0.0f;
168 d->path = NULL;
169 d->file = NULL;
140} 170}
141 171
142void deinit_GmDownload(iGmDownload *d) { 172void deinit_GmDownload(iGmDownload *d) {
173 closeFile_GmDownload_(d);
143 deinit_GmMediaProps_(&d->props); 174 deinit_GmMediaProps_(&d->props);
175 delete_String(d->path);
176}
177
178static void writeToFile_GmDownload_(iGmDownload *d, const iBlock *data) {
179 const static unsigned rateInterval_ = 1000;
180 iAssert(d->file);
181 writeData_File(d->file,
182 constBegin_Block(data) + d->numBytes,
183 size_Block(data) - d->numBytes);
184 const size_t newBytes = size_Block(data) - d->numBytes;
185 d->numBytes = size_Block(data);
186 d->rateNumBytes += newBytes;
187 const uint32_t now = SDL_GetTicks();
188 if (now - d->rateStartTime > rateInterval_) {
189 const double elapsed = (double) (now - d->rateStartTime) / 1000.0;
190 d->rateStartTime = now;
191 d->currentRate = (float) (d->rateNumBytes / elapsed);
192 d->rateNumBytes = 0;
193 }
144} 194}
145 195
146iDefineTypeConstruction(GmDownload) 196iDefineTypeConstruction(GmDownload)
@@ -183,7 +233,7 @@ void clear_Media(iMedia *d) {
183 clear_PtrArray(&d->downloads); 233 clear_PtrArray(&d->downloads);
184} 234}
185 235
186iBool setUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) { 236iBool setDownloadUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) {
187 iGmDownload *dl = NULL; 237 iGmDownload *dl = NULL;
188 iMediaId existing = findLinkDownload_Media(d, linkId); 238 iMediaId existing = findLinkDownload_Media(d, linkId);
189 iBool isNew = iFalse; 239 iBool isNew = iFalse;
@@ -251,8 +301,16 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
251 } 301 }
252 else { 302 else {
253 dl = at_PtrArray(&d->downloads, existing - 1); 303 dl = at_PtrArray(&d->downloads, existing - 1);
254 iAssert(equal_String(&dl->props.mime, mime)); /* MIME cannot change */ 304 if (isEmpty_String(&dl->props.mime)) {
255 /* TODO: Write data chunk to file. */ 305 set_String(&dl->props.mime, mime);
306 }
307 if (!dl->file) {
308 openFile_GmDownload_(dl);
309 }
310 writeToFile_GmDownload_(dl, data);
311 if (!isPartial) {
312 closeFile_GmDownload_(dl);
313 }
256 } 314 }
257 } 315 }
258 else if (!isDeleting) { 316 else if (!isDeleting) {
@@ -378,6 +436,33 @@ iPlayer *audioPlayer_Media(const iMedia *d, iMediaId audioId) {
378 return NULL; 436 return NULL;
379} 437}
380 438
439iBool downloadInfo_Media(const iMedia *d, iMediaId downloadId, iGmMediaInfo *info_out) {
440 if (downloadId > 0 && downloadId <= size_PtrArray(&d->downloads)) {
441 const iGmDownload *dl = constAt_PtrArray(&d->downloads, downloadId - 1);
442 info_out->type = cstr_String(&dl->props.mime);
443 info_out->isPermanent = dl->props.isPermanent;
444 info_out->numBytes = dl->numBytes;
445 return iTrue;
446 }
447 iZap(*info_out);
448 return iFalse;
449}
450
451void downloadStats_Media(const iMedia *d, iMediaId downloadId, const iString **path_out,
452 float *bytesPerSecond_out, iBool *isFinished_out) {
453 *path_out = NULL;
454 *bytesPerSecond_out = 0.0f;
455 *isFinished_out = iFalse;
456 if (downloadId > 0 && downloadId <= size_PtrArray(&d->downloads)) {
457 const iGmDownload *dl = constAt_PtrArray(&d->downloads, downloadId - 1);
458 if (dl->path) {
459 *path_out = dl->path;
460 }
461 *bytesPerSecond_out = dl->currentRate;
462 *isFinished_out = (dl->path && !dl->file);
463 }
464}
465
381/*----------------------------------------------------------------------------------------------*/ 466/*----------------------------------------------------------------------------------------------*/
382 467
383static void updated_MediaRequest_(iAnyObject *obj) { 468static void updated_MediaRequest_(iAnyObject *obj) {