BASIC User Group > Code Challenges

Programatically download the latest 64 bit version of Firefox for Windows.

<< < (2/11) > >>

Based on your feedback, I have the right installer file and I'm stuck with after the fact download stats without enhancing the libcurl extension module.

On a positive note, a 2.4 second delay before completion and results isn't eternity.


--- Code: C ---/*************************************************************************** *                                  _   _ ____  _ *  Project                     ___| | | |  _ \| | *                             / __| | | | |_) | | *                            | (__| |_| |  _ <| |___ *                             \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2018, Daniel Stenberg, <>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ /* <DESC> * Use the progress callbacks, old and/or new one depending on available * libcurl version. * </DESC> */ #include <stdio.h>#include <curl/curl.h> #if LIBCURL_VERSION_NUM >= 0x073d00/* In libcurl 7.61.0, support was added for extracting the time in plain   microseconds. Older libcurl versions are stuck in using 'double' for this   information so we complicate this example a bit by supporting either   approach. */ #define TIME_IN_US 1  #define TIMETYPE curl_off_t#define TIMEOPT CURLINFO_TOTAL_TIME_T#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL     3000000#else#define TIMETYPE double#define TIMEOPT CURLINFO_TOTAL_TIME#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL     3#endif #define STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES         6000 struct myprogress {  TIMETYPE lastruntime; /* type depends on version, see above */   CURL *curl;}; /* this is how the CURLOPT_XFERINFOFUNCTION callback works */ static int xferinfo(void *p,                    curl_off_t dltotal, curl_off_t dlnow,                    curl_off_t ultotal, curl_off_t ulnow){  struct myprogress *myp = (struct myprogress *)p;  CURL *curl = myp->curl;  TIMETYPE curtime = 0;   curl_easy_getinfo(curl, TIMEOPT, &curtime);   /* under certain circumstances it may be desirable for certain functionality     to only run every N seconds, in order to do this the transaction time can     be used */   if((curtime - myp->lastruntime) >= MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL) {    myp->lastruntime = curtime;#ifdef TIME_IN_US    fprintf(stderr, "TOTAL TIME: %" CURL_FORMAT_CURL_OFF_T ".%06ld\r\n",            (curtime / 1000000), (long)(curtime % 1000000));#else    fprintf(stderr, "TOTAL TIME: %f \r\n", curtime);#endif  }   fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T          "  DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T          "\r\n",          ulnow, ultotal, dlnow, dltotal);   if(dlnow > STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES)    return 1;  return 0;} #if LIBCURL_VERSION_NUM < 0x072000/* for libcurl older than 7.32.0 (CURLOPT_PROGRESSFUNCTION) */ static int older_progress(void *p,                          double dltotal, double dlnow,                          double ultotal, double ulnow){  return xferinfo(p,                  (curl_off_t)dltotal,                  (curl_off_t)dlnow,                  (curl_off_t)ultotal,                  (curl_off_t)ulnow);}#endif int main(void){  CURL *curl;  CURLcode res = CURLE_OK;  struct myprogress prog;   curl = curl_easy_init();  if(curl) {    prog.lastruntime = 0;    prog.curl = curl;     curl_easy_setopt(curl, CURLOPT_URL, ""); #if LIBCURL_VERSION_NUM >= 0x072000    /* xferinfo was introduced in 7.32.0, no earlier libcurl versions will       compile as they won't have the symbols around.        If built with a newer libcurl, but running with an older libcurl:       curl_easy_setopt() will fail in run-time trying to set the new       callback, making the older callback get used.        New libcurls will prefer the new callback and instead use that one even       if both callbacks are set. */      curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);    /* pass the struct pointer into the xferinfo function, note that this is       an alias to CURLOPT_PROGRESSDATA */     curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);#else    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, older_progress);    /* pass the struct pointer into the progress function */     curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog);#endif     curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);    res = curl_easy_perform(curl);     if(res != CURLE_OK)      fprintf(stderr, "%s\n", curl_easy_strerror(res));     /* always cleanup */     curl_easy_cleanup(curl);  }  return (int)res;} 

Script BASIC

--- Code: Script BASIC ---' Firefox Download Challenge (Script BASIC Version) by JRS. INCLUDE curl.bas ch = curl::init()curl::option(ch,"URL","")wp = curl::perform(ch) IF wp LIKE "*data-latest-firefox=\"*\" data-esr-versions*" THEN  version = JOKER(2)  PRINT "Downloading Latest 64Bit Firefox (",version,") for Windows.\n"END IF IF wp LIKE """*<div id="other-platforms">*<li class="os_win64">*<a href="*"*""" THEN  curl::option(ch,"URL", JOKER(4))END IF dl_html = curl::perform(ch)IF dl_html LIKE """*href="*"*""" THEN dl_file = JOKER(2) curl::option(ch,"URL", dl_file)curl::option(ch,"FILE","Firefox_Setup-" & version & ".exe")curl::option(ch,"NOPROGRESS",0)curl::perform(ch) PRINTNLPRINT "Firefox_Setup-" & version & ".exe Downloaded ",FORMAT("%~##,###,###~ Bytes",curl::info(ch,"SIZE_DOWNLOAD")), _  " at ",FORMAT("%~##,###,###~ Bytes/Second",curl::info(ch,"SPEED_DOWNLOAD")),".\n" curl::finish(ch) 

jrs@jrs-laptop:~/sb/abcc$ time scriba
Downloading Latest 64Bit Firefox (63.0) for Windows.
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 42.3M  100 42.3M    0     0  24.9M      0  0:00:01  0:00:01 --:--:-- 27.7M

Firefox_Setup-63.0.exe Downloaded 44,400,072 Bytes at 26,133,061 Bytes/Second.

real   0m2.863s
user   0m1.213s
sys   0m0.276s

Curious how big that Nim executable is with all those dependency requirements?

--- Code: ---import httpclient, htmlparser,xmlparser, xmltree, streams, ospaths, asyncdispatch, strutils

--- End code ---

That's because the core Nim compiler is designed to be fairly lean.

Similar to SB/Python/Ruby/FreePascal/Delphi/Xojo(RealBasic)/Powershell/Lua/ZShell/C#, it has a concept of a "Modules" system whereby additional functionality can be added to a given programming language without having it all baked in, or having to recompile the core to add that functionality.

Not having all functionality baked in also allows one to alter a given module in order to achieve a desired result.  SB's Curl module is a perfect example, where it doesn't currently support Curl Callbacks, but that feature can be added without messing with any of the core stuff.


I was curious how difficult this would be in POWERSHELL, so here's my implementation:

--- Code: PowerShell ---Import-Module BitsTransfer Clear-Host echo "Firefox Download Challenge (Powershell Version) by AIR.`r`n" $progressPreference = 'silentlyContinue'$result = Invoke-WebRequest ""$progressPreference = 'Continue' $FF_Version = ($result | %{ $_ -match 'data-latest-firefox="(.+?)"'}) | %{$Matches[1]} echo "Downloading Latest 64Bit Firefox ($FF_Version) for Windows.`r`n" $HTML = $result.ParsedHtml.getElementsByTagName('li') $hits = $HTML | where {$_.outerHTML -match "class=os_win64"} $download_link = ($hits | where {$_.outerTEXT -match 'Windows 64-bit'}).innerHTML | %{[regex]::matches($_,'(?<=\").+?(?=\")')[0].value} Start-BitsTransfer -Source $download_link -Destination "Firefox Setup $FF_VERSION.exe" echo "Download Complete.`r`n"  
Note that the "BitsTransfer" module provides a console-based progress bar, so technically this doesn't meet the full requirements of the challenge.  But it was fun to code!



[0] Message Index

[#] Next page

[*] Previous page

Go to full version