mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-02-18 04:45:39 +00:00
Added a download percentage indicator to the application updater together with some other minor improvements
Also cleaned up HttpReq in general and added a progress meter callback
This commit is contained in:
parent
cd2181a8b5
commit
f91a87251d
|
@ -125,10 +125,11 @@ bool ApplicationUpdater::downloadFile()
|
||||||
mMaxTime = mTimer + (MAX_DOWNLOAD_TIME * 1000);
|
mMaxTime = mTimer + (MAX_DOWNLOAD_TIME * 1000);
|
||||||
|
|
||||||
mStatus = ASYNC_IN_PROGRESS;
|
mStatus = ASYNC_IN_PROGRESS;
|
||||||
mRequest = std::unique_ptr<HttpReq>(std::make_unique<HttpReq>(mUrl));
|
mRequest = std::unique_ptr<HttpReq>(std::make_unique<HttpReq>(mUrl, false));
|
||||||
|
|
||||||
while (mTimer < mMaxTime || !mAbortDownload) {
|
while (mTimer < mMaxTime || !mAbortDownload) {
|
||||||
SDL_Delay(10);
|
// Add a small delay so we don't eat all CPU cycles checking for status updates.
|
||||||
|
SDL_Delay(5);
|
||||||
try {
|
try {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,15 @@
|
||||||
#include "guis/GuiTextEditKeyboardPopup.h"
|
#include "guis/GuiTextEditKeyboardPopup.h"
|
||||||
#include "utils/PlatformUtil.h"
|
#include "utils/PlatformUtil.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL_timer.h>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
GuiApplicationUpdater::GuiApplicationUpdater()
|
GuiApplicationUpdater::GuiApplicationUpdater()
|
||||||
: mRenderer {Renderer::getInstance()}
|
: mRenderer {Renderer::getInstance()}
|
||||||
, mBackground {":/graphics/frame.svg"}
|
, mBackground {":/graphics/frame.svg"}
|
||||||
, mGrid {glm::ivec2 {4, 11}}
|
, mGrid {glm::ivec2 {4, 11}}
|
||||||
|
, mDownloadPercentage {0}
|
||||||
, mLinuxAppImage {false}
|
, mLinuxAppImage {false}
|
||||||
, mAbortDownload {false}
|
, mAbortDownload {false}
|
||||||
, mDownloading {false}
|
, mDownloading {false}
|
||||||
|
@ -97,6 +100,7 @@ GuiApplicationUpdater::GuiApplicationUpdater()
|
||||||
}
|
}
|
||||||
mMessage = "";
|
mMessage = "";
|
||||||
mStatusMessage->setText(mMessage);
|
mStatusMessage->setText(mMessage);
|
||||||
|
mDownloadPercentage = 0;
|
||||||
mDownloading = true;
|
mDownloading = true;
|
||||||
if (mThread) {
|
if (mThread) {
|
||||||
mThread->join();
|
mThread->join();
|
||||||
|
@ -158,6 +162,10 @@ GuiApplicationUpdater::GuiApplicationUpdater()
|
||||||
|
|
||||||
mButton3 = std::make_shared<ButtonComponent>("CANCEL", "cancel", [this]() {
|
mButton3 = std::make_shared<ButtonComponent>("CANCEL", "cancel", [this]() {
|
||||||
mAbortDownload = true;
|
mAbortDownload = true;
|
||||||
|
if (mThread) {
|
||||||
|
mThread->join();
|
||||||
|
mThread.reset();
|
||||||
|
}
|
||||||
if (mDownloading) {
|
if (mDownloading) {
|
||||||
mWindow->pushGui(
|
mWindow->pushGui(
|
||||||
new GuiMsgBox(getHelpStyle(), "DOWNLOAD ABORTED\nNO PACKAGE SAVED TO DISK", "OK",
|
new GuiMsgBox(getHelpStyle(), "DOWNLOAD ABORTED\nNO PACKAGE SAVED TO DISK", "OK",
|
||||||
|
@ -166,7 +174,7 @@ GuiApplicationUpdater::GuiApplicationUpdater()
|
||||||
0.70f :
|
0.70f :
|
||||||
0.45f * (1.778f / mRenderer->getScreenAspectRatio()))));
|
0.45f * (1.778f / mRenderer->getScreenAspectRatio()))));
|
||||||
}
|
}
|
||||||
else if (mHasDownloaded && !mHasInstalled) {
|
else if (mHasDownloaded || mReadyToInstall) {
|
||||||
mWindow->pushGui(new GuiMsgBox(
|
mWindow->pushGui(new GuiMsgBox(
|
||||||
getHelpStyle(), "PACKAGE WAS DOWNLOADED AND\nCAN BE MANUALLY INSTALLED", "OK",
|
getHelpStyle(), "PACKAGE WAS DOWNLOADED AND\nCAN BE MANUALLY INSTALLED", "OK",
|
||||||
nullptr, "", nullptr, "", nullptr, true, true,
|
nullptr, "", nullptr, "", nullptr, true, true,
|
||||||
|
@ -201,7 +209,7 @@ GuiApplicationUpdater::GuiApplicationUpdater()
|
||||||
std::round(mRenderer->getScreenHeight() * 0.13f));
|
std::round(mRenderer->getScreenHeight() * 0.13f));
|
||||||
|
|
||||||
mBusyAnim.setSize(mSize);
|
mBusyAnim.setSize(mSize);
|
||||||
mBusyAnim.setText("DOWNLOADING");
|
mBusyAnim.setText("DOWNLOADING 100%");
|
||||||
mBusyAnim.onSizeChanged();
|
mBusyAnim.onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,11 +248,12 @@ void GuiApplicationUpdater::setDownloadPath()
|
||||||
bool GuiApplicationUpdater::downloadPackage()
|
bool GuiApplicationUpdater::downloadPackage()
|
||||||
{
|
{
|
||||||
mStatus = ASYNC_IN_PROGRESS;
|
mStatus = ASYNC_IN_PROGRESS;
|
||||||
mRequest = std::unique_ptr<HttpReq>(std::make_unique<HttpReq>(mPackage.url));
|
mRequest = std::unique_ptr<HttpReq>(std::make_unique<HttpReq>(mPackage.url, false));
|
||||||
LOG(LogDebug) << "GuiApplicationUpdater::downloadPackage(): Starting download of \""
|
LOG(LogInfo) << "Downloading \"" << mPackage.filename << "\"...";
|
||||||
<< mPackage.filename << "\"";
|
|
||||||
|
|
||||||
while (!mAbortDownload) {
|
while (!mAbortDownload) {
|
||||||
|
// Add a small delay so we don't eat all CPU cycles checking for status updates.
|
||||||
|
SDL_Delay(5);
|
||||||
HttpReq::Status reqStatus {mRequest->status()};
|
HttpReq::Status reqStatus {mRequest->status()};
|
||||||
if (reqStatus == HttpReq::REQ_SUCCESS) {
|
if (reqStatus == HttpReq::REQ_SUCCESS) {
|
||||||
mStatus = ASYNC_DONE;
|
mStatus = ASYNC_DONE;
|
||||||
|
@ -260,6 +269,14 @@ bool GuiApplicationUpdater::downloadPackage()
|
||||||
mMessage = errorMessage;
|
mMessage = errorMessage;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Download progress as reported by curl.
|
||||||
|
const float downloadedBytes {static_cast<float>(mRequest->getDownloadedBytes())};
|
||||||
|
const float totalBytes {static_cast<float>(mRequest->getTotalBytes())};
|
||||||
|
if (downloadedBytes != 0.0f && totalBytes != 0.0f)
|
||||||
|
mDownloadPercentage =
|
||||||
|
static_cast<int>(std::round((downloadedBytes / totalBytes) * 100.0f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAbortDownload) {
|
if (mAbortDownload) {
|
||||||
|
@ -304,10 +321,10 @@ bool GuiApplicationUpdater::downloadPackage()
|
||||||
writeFile.open(mDownloadPackageFilename.c_str(), std::ofstream::binary);
|
writeFile.open(mDownloadPackageFilename.c_str(), std::ofstream::binary);
|
||||||
|
|
||||||
if (writeFile.fail()) {
|
if (writeFile.fail()) {
|
||||||
const std::string errorMessage {"Couldn't write package file, permission problems?"};
|
LOG(LogError) << "Couldn't write package file \"" << mDownloadPackageFilename
|
||||||
LOG(LogError) << errorMessage;
|
<< "\", permission problems?";
|
||||||
std::unique_lock<std::mutex> lock {mMutex};
|
std::unique_lock<std::mutex> lock {mMutex};
|
||||||
mMessage = "Error: " + errorMessage;
|
mMessage = "Error: Couldn't write package file, permission problems?";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,8 +457,10 @@ void GuiApplicationUpdater::update(int deltaTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDownloading)
|
if (mDownloading) {
|
||||||
|
mBusyAnim.setText("DOWNLOADING " + std::to_string(mDownloadPercentage) + "%");
|
||||||
mBusyAnim.update(deltaTime);
|
mBusyAnim.update(deltaTime);
|
||||||
|
}
|
||||||
else if (mLinuxAppImage && mReadyToInstall) {
|
else if (mLinuxAppImage && mReadyToInstall) {
|
||||||
mProcessStep1->setText(ViewController::TICKMARK_CHAR + " " + mProcessStep1->getValue());
|
mProcessStep1->setText(ViewController::TICKMARK_CHAR + " " + mProcessStep1->getValue());
|
||||||
mProcessStep1->setColor(mMenuColorGreen);
|
mProcessStep1->setColor(mMenuColorGreen);
|
||||||
|
|
|
@ -73,6 +73,7 @@ private:
|
||||||
|
|
||||||
ApplicationUpdater::Package mPackage;
|
ApplicationUpdater::Package mPackage;
|
||||||
std::string mDownloadPackageFilename;
|
std::string mDownloadPackageFilename;
|
||||||
|
std::atomic<int> mDownloadPercentage;
|
||||||
std::atomic<bool> mLinuxAppImage;
|
std::atomic<bool> mLinuxAppImage;
|
||||||
std::atomic<bool> mAbortDownload;
|
std::atomic<bool> mAbortDownload;
|
||||||
std::atomic<bool> mDownloading;
|
std::atomic<bool> mDownloading;
|
||||||
|
|
|
@ -573,7 +573,7 @@ void GuiScraperSearch::updateInfoPane()
|
||||||
// through the result list.
|
// through the result list.
|
||||||
mThumbnailReqMap.insert(std::pair<std::string, std::unique_ptr<HttpReq>>(
|
mThumbnailReqMap.insert(std::pair<std::string, std::unique_ptr<HttpReq>>(
|
||||||
mScraperResults[i].thumbnailImageUrl,
|
mScraperResults[i].thumbnailImageUrl,
|
||||||
std::unique_ptr<HttpReq>(new HttpReq(thumb))));
|
std::unique_ptr<HttpReq>(new HttpReq(thumb, true))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,7 +158,7 @@ std::unique_ptr<HttpReq> TheGamesDBJSONRequestResources::fetchResource(const std
|
||||||
std::string path {"https://api.thegamesdb.net/v1"};
|
std::string path {"https://api.thegamesdb.net/v1"};
|
||||||
path.append(endpoint).append("?apikey=").append(getApiKey());
|
path.append(endpoint).append("?apikey=").append(getApiKey());
|
||||||
|
|
||||||
return std::unique_ptr<HttpReq>(new HttpReq(path));
|
return std::unique_ptr<HttpReq>(new HttpReq(path, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
int TheGamesDBJSONRequestResources::loadResource(std::unordered_map<int, std::string>& resource,
|
int TheGamesDBJSONRequestResources::loadResource(std::unordered_map<int, std::string>& resource,
|
||||||
|
|
|
@ -142,7 +142,7 @@ ScraperHttpRequest::ScraperHttpRequest(std::vector<ScraperSearchResult>& results
|
||||||
: ScraperRequest(resultsWrite)
|
: ScraperRequest(resultsWrite)
|
||||||
{
|
{
|
||||||
setStatus(ASYNC_IN_PROGRESS);
|
setStatus(ASYNC_IN_PROGRESS);
|
||||||
mReq = std::unique_ptr<HttpReq>(new HttpReq(url));
|
mReq = std::unique_ptr<HttpReq>(new HttpReq(url, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScraperHttpRequest::update()
|
void ScraperHttpRequest::update()
|
||||||
|
@ -426,7 +426,7 @@ MediaDownloadHandle::MediaDownloadHandle(const std::string& url,
|
||||||
const std::string& mediaType,
|
const std::string& mediaType,
|
||||||
const bool resizeFile,
|
const bool resizeFile,
|
||||||
bool& savedNewMedia)
|
bool& savedNewMedia)
|
||||||
: mReq(new HttpReq(url))
|
: mReq(new HttpReq(url, true))
|
||||||
, mSavePath(path)
|
, mSavePath(path)
|
||||||
, mExistingMediaFile(existingMediaPath)
|
, mExistingMediaFile(existingMediaPath)
|
||||||
, mMediaType(mediaType)
|
, mMediaType(mediaType)
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
// EmulationStation Desktop Edition
|
// EmulationStation Desktop Edition
|
||||||
// HttpReq.cpp
|
// HttpReq.cpp
|
||||||
//
|
//
|
||||||
// HTTP request functions.
|
// HTTP requests using libcurl.
|
||||||
// Used by Scraper, GamesDBJSONScraper, GamesDBJSONScraperResources and
|
// Used by the scraper and application updater.
|
||||||
// ScreenScraper to download game information and media files.
|
|
||||||
// Also used by ApplicationUpdater to check for application updates.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "HttpReq.h"
|
#include "HttpReq.h"
|
||||||
|
@ -38,17 +36,11 @@ std::string HttpReq::urlEncode(const std::string& s)
|
||||||
return escaped;
|
return escaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpReq::isUrl(const std::string& str)
|
HttpReq::HttpReq(const std::string& url, bool scraperRequest)
|
||||||
{
|
|
||||||
// The worst guess.
|
|
||||||
return (!str.empty() && !Utils::FileSystem::exists(str) &&
|
|
||||||
(str.find("http://") != std::string::npos ||
|
|
||||||
str.find("https://") != std::string::npos || str.find("www.") != std::string::npos));
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpReq::HttpReq(const std::string& url)
|
|
||||||
: mStatus(REQ_IN_PROGRESS)
|
: mStatus(REQ_IN_PROGRESS)
|
||||||
, mHandle(nullptr)
|
, mHandle(nullptr)
|
||||||
|
, mTotalBytes {0}
|
||||||
|
, mDownloadedBytes {0}
|
||||||
{
|
{
|
||||||
// The multi-handle is cleaned up via a call from GuiScraperSearch after the scraping
|
// The multi-handle is cleaned up via a call from GuiScraperSearch after the scraping
|
||||||
// has been completed for a game, meaning the handle is valid for all curl requests
|
// has been completed for a game, meaning the handle is valid for all curl requests
|
||||||
|
@ -87,12 +79,19 @@ HttpReq::HttpReq(const std::string& url)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long connectionTimeout {
|
long connectionTimeout;
|
||||||
static_cast<long>(Settings::getInstance()->getInt("ScraperConnectionTimeout"))};
|
|
||||||
|
|
||||||
if (connectionTimeout < 0 || connectionTimeout > 300)
|
if (scraperRequest) {
|
||||||
connectionTimeout =
|
connectionTimeout =
|
||||||
static_cast<long>(Settings::getInstance()->getDefaultInt("ScraperConnectionTimeout"));
|
static_cast<long>(Settings::getInstance()->getInt("ScraperConnectionTimeout"));
|
||||||
|
|
||||||
|
if (connectionTimeout < 0 || connectionTimeout > 300)
|
||||||
|
connectionTimeout = static_cast<long>(
|
||||||
|
Settings::getInstance()->getDefaultInt("ScraperConnectionTimeout"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
connectionTimeout = 30;
|
||||||
|
}
|
||||||
|
|
||||||
// Set connection timeout (default is 30 seconds).
|
// Set connection timeout (default is 30 seconds).
|
||||||
err = curl_easy_setopt(mHandle, CURLOPT_CONNECTTIMEOUT, connectionTimeout);
|
err = curl_easy_setopt(mHandle, CURLOPT_CONNECTTIMEOUT, connectionTimeout);
|
||||||
|
@ -102,14 +101,21 @@ HttpReq::HttpReq(const std::string& url)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long transferTimeout {
|
long transferTimeout;
|
||||||
static_cast<long>(Settings::getInstance()->getInt("ScraperTransferTimeout"))};
|
|
||||||
|
|
||||||
if (transferTimeout < 0 || transferTimeout > 300)
|
if (scraperRequest) {
|
||||||
transferTimeout =
|
transferTimeout =
|
||||||
static_cast<long>(Settings::getInstance()->getDefaultInt("ScraperTransferTimeout"));
|
static_cast<long>(Settings::getInstance()->getInt("ScraperTransferTimeout"));
|
||||||
|
|
||||||
// Set transfer timeout (default is 120 seconds).
|
if (transferTimeout < 0 || transferTimeout > 300)
|
||||||
|
transferTimeout =
|
||||||
|
static_cast<long>(Settings::getInstance()->getDefaultInt("ScraperTransferTimeout"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
transferTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set transfer timeout (default is 120 seconds for the scraper and infinite otherwise).
|
||||||
err = curl_easy_setopt(mHandle, CURLOPT_TIMEOUT, transferTimeout);
|
err = curl_easy_setopt(mHandle, CURLOPT_TIMEOUT, transferTimeout);
|
||||||
if (err != CURLE_OK) {
|
if (err != CURLE_OK) {
|
||||||
mStatus = REQ_IO_ERROR;
|
mStatus = REQ_IO_ERROR;
|
||||||
|
@ -134,7 +140,6 @@ HttpReq::HttpReq(const std::string& url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set curl restrict redirect protocols.
|
// Set curl restrict redirect protocols.
|
||||||
|
|
||||||
#if defined(__APPLE__) || LIBCURL_VERSION_MAJOR < 7 || \
|
#if defined(__APPLE__) || LIBCURL_VERSION_MAJOR < 7 || \
|
||||||
(LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 85)
|
(LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 85)
|
||||||
err = curl_easy_setopt(mHandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
err = curl_easy_setopt(mHandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||||
|
@ -156,7 +161,7 @@ HttpReq::HttpReq(const std::string& url)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give curl a pointer to this HttpReq so we know where to write the data to in our
|
// Pass curl a pointer to this HttpReq so we know where to write the data to in our
|
||||||
// write function.
|
// write function.
|
||||||
err = curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, this);
|
err = curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, this);
|
||||||
if (err != CURLE_OK) {
|
if (err != CURLE_OK) {
|
||||||
|
@ -165,8 +170,32 @@ HttpReq::HttpReq(const std::string& url)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable the curl progress meter.
|
||||||
|
err = curl_easy_setopt(mHandle, CURLOPT_NOPROGRESS, 0);
|
||||||
|
if (err != CURLE_OK) {
|
||||||
|
mStatus = REQ_IO_ERROR;
|
||||||
|
onError(curl_easy_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass curl a pointer to HttpReq to provide access to the counter variables.
|
||||||
|
err = curl_easy_setopt(mHandle, CURLOPT_XFERINFODATA, this);
|
||||||
|
if (err != CURLE_OK) {
|
||||||
|
mStatus = REQ_IO_ERROR;
|
||||||
|
onError(curl_easy_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress meter callback.
|
||||||
|
err = curl_easy_setopt(mHandle, CURLOPT_XFERINFOFUNCTION, HttpReq::transferProgress);
|
||||||
|
if (err != CURLE_OK) {
|
||||||
|
mStatus = REQ_IO_ERROR;
|
||||||
|
onError(curl_easy_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Add the handle to our multi.
|
// Add the handle to our multi.
|
||||||
CURLMcode merr = curl_multi_add_handle(sMultiHandle, mHandle);
|
CURLMcode merr {curl_multi_add_handle(sMultiHandle, mHandle)};
|
||||||
if (merr != CURLM_OK) {
|
if (merr != CURLM_OK) {
|
||||||
mStatus = REQ_IO_ERROR;
|
mStatus = REQ_IO_ERROR;
|
||||||
onError(curl_multi_strerror(merr));
|
onError(curl_multi_strerror(merr));
|
||||||
|
@ -238,13 +267,24 @@ std::string HttpReq::getContent() const
|
||||||
return mContent.str();
|
return mContent.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used as a curl callback.
|
int HttpReq::transferProgress(
|
||||||
// size = size of an element, nmemb = number of elements.
|
void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||||
// Return value is number of elements successfully read.
|
{
|
||||||
|
// Note that it's not guaranteed that the server will actually provide the total size.
|
||||||
|
if (dltotal > 0)
|
||||||
|
static_cast<HttpReq*>(clientp)->mTotalBytes = dltotal;
|
||||||
|
if (dlnow > 0)
|
||||||
|
static_cast<HttpReq*>(clientp)->mDownloadedBytes = dlnow;
|
||||||
|
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
size_t HttpReq::writeContent(void* buff, size_t size, size_t nmemb, void* req_ptr)
|
size_t HttpReq::writeContent(void* buff, size_t size, size_t nmemb, void* req_ptr)
|
||||||
{
|
{
|
||||||
|
// size = size of an element, nmemb = number of elements.
|
||||||
std::stringstream& ss {static_cast<HttpReq*>(req_ptr)->mContent};
|
std::stringstream& ss {static_cast<HttpReq*>(req_ptr)->mContent};
|
||||||
ss.write(static_cast<char*>(buff), size * nmemb);
|
ss.write(static_cast<char*>(buff), size * nmemb);
|
||||||
|
|
||||||
|
// Return value is number of elements successfully read.
|
||||||
return nmemb;
|
return nmemb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,44 +3,23 @@
|
||||||
// EmulationStation Desktop Edition
|
// EmulationStation Desktop Edition
|
||||||
// HttpReq.h
|
// HttpReq.h
|
||||||
//
|
//
|
||||||
// HTTP request functions.
|
// HTTP requests using libcurl.
|
||||||
// Used by Scraper, GamesDBJSONScraper, GamesDBJSONScraperResources and
|
// Used by the scraper and application updater.
|
||||||
// ScreenScraper to download game information and media files.
|
|
||||||
// Also used by ApplicationUpdater to check for application updates.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ES_CORE_HTTP_REQ_H
|
#ifndef ES_CORE_HTTP_REQ_H
|
||||||
#define ES_CORE_HTTP_REQ_H
|
#define ES_CORE_HTTP_REQ_H
|
||||||
|
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
// Usage:
|
|
||||||
// HttpReq myRequest("www.duckduckgo.com", "/index.html");
|
|
||||||
//
|
|
||||||
// For blocking behavior:
|
|
||||||
// while (myRequest.status() == HttpReq::REQ_IN_PROGRESS);
|
|
||||||
//
|
|
||||||
// For non-blocking behavior:
|
|
||||||
// Check 'if (myRequest.status() != HttpReq::REQ_IN_PROGRESS)' in some sort of update method.
|
|
||||||
//
|
|
||||||
// Once one of those calls complete, the request is ready.
|
|
||||||
//
|
|
||||||
// Do something like this to capture errors:
|
|
||||||
// if (myRequest.status() != REQ_SUCCESS) {
|
|
||||||
// // An error occured.
|
|
||||||
// LOG(LogError) << "HTTP request error - " << myRequest.getErrorMessage();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// This is how to read the returned content:
|
|
||||||
// std::string content = myRequest.getContent();
|
|
||||||
|
|
||||||
class HttpReq
|
class HttpReq
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HttpReq(const std::string& url);
|
HttpReq(const std::string& url, bool scraperRequest);
|
||||||
~HttpReq();
|
~HttpReq();
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
|
@ -55,13 +34,15 @@ public:
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
Status status(); // Process any received data and return the status afterwards.
|
// Process any received data and return the status afterwards.
|
||||||
|
Status status();
|
||||||
|
|
||||||
std::string getErrorMsg() { return mErrorMsg; }
|
std::string getErrorMsg() { return mErrorMsg; }
|
||||||
std::string getContent() const; // mStatus must be REQ_SUCCESS.
|
std::string getContent() const;
|
||||||
|
long getTotalBytes() { return mTotalBytes; }
|
||||||
|
long getDownloadedBytes() { return mDownloadedBytes; }
|
||||||
|
|
||||||
static std::string urlEncode(const std::string& s);
|
static std::string urlEncode(const std::string& s);
|
||||||
static bool isUrl(const std::string& s);
|
|
||||||
|
|
||||||
static void cleanupCurlMulti()
|
static void cleanupCurlMulti()
|
||||||
{
|
{
|
||||||
if (sMultiHandle != nullptr) {
|
if (sMultiHandle != nullptr) {
|
||||||
|
@ -71,7 +52,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Callbacks.
|
||||||
|
static int transferProgress(
|
||||||
|
void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
|
||||||
static size_t writeContent(void* buff, size_t size, size_t nmemb, void* req_ptr);
|
static size_t writeContent(void* buff, size_t size, size_t nmemb, void* req_ptr);
|
||||||
|
|
||||||
void onError(const std::string& msg) { mErrorMsg = msg; }
|
void onError(const std::string& msg) { mErrorMsg = msg; }
|
||||||
|
|
||||||
static inline std::map<CURL*, HttpReq*> sRequests;
|
static inline std::map<CURL*, HttpReq*> sRequests;
|
||||||
|
@ -82,6 +67,8 @@ private:
|
||||||
|
|
||||||
std::stringstream mContent;
|
std::stringstream mContent;
|
||||||
std::string mErrorMsg;
|
std::string mErrorMsg;
|
||||||
|
std::atomic<long> mTotalBytes;
|
||||||
|
std::atomic<long> mDownloadedBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_HTTP_REQ_H
|
#endif // ES_CORE_HTTP_REQ_H
|
||||||
|
|
Loading…
Reference in a new issue