Skip to content

Commit 3775a76

Browse files
Working avatar image
1 parent 52c8d70 commit 3775a76

9 files changed

+206
-17
lines changed

src/api.cpp

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ with this program. If not, see <https://www.gnu.org/licenses/>
1717
*/
1818

1919
#include "api.hpp"
20+
#include "downloader.h"
2021
#include <plugin-support.h>
2122

2223
#include <fstream>
2324

2425
#include <obs-module.h>
2526
#include <util/platform.h>
2627
#include <nlohmann/json.hpp>
28+
#include <QDir>
2729

2830
using json = nlohmann::json;
2931

@@ -36,7 +38,10 @@ MarketplaceApi::MarketplaceApi()
3638
: _gatewayUrl(DEFAULT_GATEWAY_URL),
3739
_authUrl(DEFAULT_AUTH_URL),
3840
_storeUrl(DEFAULT_STORE_URL),
39-
_loggedIn(false)
41+
_loggedIn(false),
42+
_hasAvatar(false),
43+
_avatarReady(false),
44+
_avatarDownloading(false)
4045
{
4146
std::string dataPath = obs_get_module_data_path(obs_current_module());
4247
dataPath += "/" + std::string(API_URLS_FILE);
@@ -52,12 +57,7 @@ MarketplaceApi::MarketplaceApi()
5257
obs_log(LOG_WARNING,
5358
"Urls file found, but could not be loaded.");
5459
}
55-
} else {
56-
obs_log(LOG_INFO, "No url file found.");
5760
}
58-
obs_log(LOG_INFO, "Gateway Url: %s", _gatewayUrl.c_str());
59-
obs_log(LOG_INFO, "Auth Url: %s", _authUrl.c_str());
60-
obs_log(LOG_INFO, "Store Url: %s", _storeUrl.c_str());
6161
}
6262

6363
MarketplaceApi *MarketplaceApi::getInstance()
@@ -73,14 +73,82 @@ MarketplaceApi *MarketplaceApi::getInstance()
7373

7474
void MarketplaceApi::setUserDetails(nlohmann::json &data)
7575
{
76+
_hasAvatar = false;
7677
try {
7778
_firstName = data.at("first_name").template get<std::string>();
7879
_lastName = data.at("last_name").template get<std::string>();
7980
std::string color = data.at("default_avatar_color")
8081
.template get<std::string>();
8182
_avatarColor = avatarColors.at(color);
83+
if (data.contains("avatar_resolutions")) {
84+
for (auto it : data["avatar_resolutions"]) {
85+
if (it.contains("resolution") && it["resolution"] == "180x180") {
86+
_avatarUrl = it["asset_cdn"];
87+
_hasAvatar = true;
88+
std::string avatarPath = QDir::homePath().toStdString();
89+
avatarPath += "/AppData/Local/Elgato/DeepLinking/Thumbnails";
90+
os_mkdirs(avatarPath.c_str());
91+
92+
auto found = _avatarUrl.find_last_of("/");
93+
if (found == std::string::npos) {
94+
return;
95+
}
96+
auto filename = _avatarUrl.substr(found + 1);
97+
avatarPath = avatarPath + "/" + filename;
98+
std::lock_guard<std::mutex> lock(_mtx);
99+
if (!_avatarDownloading) {
100+
if (!os_file_exists(avatarPath.c_str())) {
101+
obs_log(LOG_INFO, "Downloading avatar %s", filename.c_str());
102+
_downloadAvatar();
103+
}
104+
else {
105+
obs_log(LOG_INFO, "Avatar Exists %s", filename.c_str());
106+
_avatarPath = avatarPath;
107+
_avatarReady = true;
108+
}
109+
}
110+
}
111+
}
112+
}
82113
_loggedIn = true;
83114
} catch (...) {
115+
obs_log(LOG_ERROR, "There was a problem processing the user response data.");
116+
}
117+
}
118+
119+
void MarketplaceApi::_downloadAvatar()
120+
{
121+
_avatarDownloading = true;
122+
_avatarReady = false;
123+
std::string savePath = QDir::homePath().toStdString();
124+
savePath += "/AppData/Local/Elgato/DeepLinking/Thumbnails/";
125+
std::shared_ptr<Downloader> dl = Downloader::getInstance("");
126+
dl->Enqueue(_avatarUrl, savePath, MarketplaceApi::AvatarProgress, MarketplaceApi::AvatarDownloadComplete,
127+
this);
128+
}
129+
130+
void MarketplaceApi::AvatarDownloadComplete(std::string filename, void* data)
131+
{
132+
obs_log(LOG_INFO, "Avatar Downloaded! %s", filename.c_str());
133+
auto api = static_cast<MarketplaceApi*>(data);
134+
api->_avatarReady = true;
135+
api->_avatarPath = filename;
136+
api->_avatarDownloading = false;
137+
emit api->AvatarDownloaded();
138+
}
139+
140+
void MarketplaceApi::AvatarProgress(void* ptr, bool finished,
141+
bool downloading, uint64_t fileSize,
142+
uint64_t chunkSize, uint64_t downloaded)
143+
{
144+
UNUSED_PARAMETER(ptr);
145+
//UNUSED_PARAMETER(finished);
146+
UNUSED_PARAMETER(downloading);
147+
UNUSED_PARAMETER(fileSize);
148+
UNUSED_PARAMETER(chunkSize);
149+
UNUSED_PARAMETER(downloaded);
150+
if (finished) {
151+
obs_log(LOG_INFO, "Download of avatar finished.");
84152
}
85153
}
86154

src/api.hpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
2020
#include <string>
2121
#include <mutex>
2222
#include <nlohmann/json.hpp>
23+
#include <QObject>
2324

2425
#define DEFAULT_GATEWAY_URL "https://mp-gateway.elgato.com"
2526
#define DEFAULT_STORE_URL "https://marketplace.elgato.com"
@@ -33,7 +34,8 @@ const std::map<std::string, std::string> avatarColors = {
3334
{"teal", "#22837D"}, {"cyan", "#0F7EAD"}, {"purple", "#A638FE"},
3435
{"gray", "#767676"}};
3536

36-
class MarketplaceApi {
37+
class MarketplaceApi : public QObject {
38+
Q_OBJECT
3739
public:
3840
static MarketplaceApi *getInstance();
3941
inline std::string gatewayUrl() const { return _gatewayUrl; }
@@ -42,20 +44,37 @@ class MarketplaceApi {
4244
inline std::string firstName() const { return _firstName; }
4345
inline std::string lastName() const { return _lastName; }
4446
inline std::string avatarColor() const { return _avatarColor; }
45-
47+
inline bool hasAvatar() const { return _hasAvatar; }
48+
inline std::string avatarUrl() const { return _avatarUrl; }
49+
inline bool avatarReady() const { return _avatarReady; }
50+
inline std::string avatarPath() const { return _avatarPath; }
51+
static void AvatarProgress(void* ptr, bool finished,
52+
bool downloading, uint64_t fileSize,
53+
uint64_t chunkSize, uint64_t downloaded);
54+
static void AvatarDownloadComplete(std::string filename, void* data);
4655
void setUserDetails(nlohmann::json &data);
4756

57+
signals:
58+
void AvatarDownloaded();
59+
4860
private:
4961
MarketplaceApi();
5062
MarketplaceApi(const MarketplaceApi &cpy) = delete;
5163

64+
void _downloadAvatar();
65+
5266
std::string _gatewayUrl;
5367
std::string _storeUrl;
5468
std::string _authUrl;
5569
bool _loggedIn;
70+
bool _hasAvatar;
71+
bool _avatarReady;
72+
bool _avatarDownloading;
5673
std::string _firstName;
5774
std::string _lastName;
5875
std::string _avatarColor;
76+
std::string _avatarUrl;
77+
std::string _avatarPath;
5978

6079
static MarketplaceApi *_api;
6180
static std::mutex _mtx;

src/downloader.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ size_t Downloader::DownloadEntry::handle_progress(void *ptr, curl_off_t dltotal,
137137
Downloader::DownloadEntry::DownloadEntry(Downloader *parent, std::string url,
138138
std::string targetPath,
139139
ProgressCallbackFn pc,
140+
CompleteCallbackFn cc,
140141
void *callbackDat)
141142
: url(url),
142143
file(nullptr),
@@ -146,6 +147,7 @@ Downloader::DownloadEntry::DownloadEntry(Downloader *parent, std::string url,
146147
references(0),
147148
removed(false),
148149
progressCallback(pc),
150+
completeCallback(cc),
149151
callbackData(callbackDat),
150152
parent(parent)
151153
{
@@ -235,7 +237,7 @@ void Downloader::DownloadEntry::Finish()
235237
//bfree(absPath);
236238

237239
parent->moveRequests.push_back(
238-
{tmpTargetName, target.c_str(), callbackData});
240+
{tmpTargetName, target.c_str(), callbackData, completeCallback});
239241
}
240242
if (progressCallback) {
241243
progressCallback(callbackData, true, false, fileSize, 0,
@@ -296,12 +298,12 @@ std::shared_ptr<Downloader> Downloader::getInstance(std::string configLocation)
296298
}
297299

298300
Downloader::Entry Downloader::Enqueue(std::string url, std::string targetPath,
299-
ProgressCallbackFn pc, void *callbackDat)
301+
ProgressCallbackFn pc, CompleteCallbackFn cc, void *callbackDat)
300302
{
301303
std::unique_lock l(lock);
302304

303305
auto dlentry = std::make_shared<DownloadEntry>(this, url, targetPath,
304-
pc, callbackDat);
306+
pc, cc, callbackDat);
305307
auto result = queue.emplace(idCounter++, dlentry);
306308
if (result.first == queue.end() || result.second == false) {
307309
throw "TODO";
@@ -397,8 +399,9 @@ void Downloader::workerJob()
397399
});
398400
} else {
399401
// We are downloading a thumbnail.
400-
elgatocloud::ElgatoProduct::SetThumbnail(
401-
file, mr.data);
402+
if (mr.callback) {
403+
mr.callback(file, mr.data);
404+
}
402405
}
403406
}
404407
} while (working);

src/downloader.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@ with this program. If not, see <https://www.gnu.org/licenses/>
4242
typedef void (*ProgressCallbackFn)(void *, bool, bool, uint64_t, uint64_t,
4343
uint64_t);
4444

45+
typedef void (*CompleteCallbackFn)(std::string, void* data);
46+
4547
struct MoveRequestData {
4648
std::string first;
4749
std::string second;
4850
void *data;
51+
CompleteCallbackFn callback;
4952
};
5053

5154
class Downloader {
@@ -79,11 +82,13 @@ class Downloader {
7982
CURL *handle;
8083
Downloader *parent;
8184
ProgressCallbackFn progressCallback;
85+
CompleteCallbackFn completeCallback;
8286
void *callbackData;
8387

8488
DownloadEntry(Downloader *parent, std::string url,
8589
std::string targetPath,
8690
ProgressCallbackFn pc = nullptr,
91+
CompleteCallbackFn cc = nullptr,
8792
void *callbackDat = nullptr);
8893
~DownloadEntry();
8994
void Finish();
@@ -152,6 +157,7 @@ class Downloader {
152157
// If targetPath ends with a / or \ character, the name will be automatically set
153158
Entry Enqueue(std::string url, std::string targetPath = "",
154159
ProgressCallbackFn pc = nullptr,
160+
CompleteCallbackFn cc = nullptr,
155161
void *callbackDat = nullptr);
156162
Entry Lookup(size_t id);
157163
std::vector<Entry> Enumerate(size_t limit = -1);

src/elgato-cloud-data.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ void ElgatoCloud::_LoadUserData(bool loadData)
450450
api_url += "/user";
451451
auto userResponse =
452452
fetch_string_from_get(api_url, _accessToken);
453+
obs_log(LOG_INFO, "User Response: %s", userResponse.c_str());
453454
auto userData = nlohmann::json::parse(userResponse);
454455
api->setUserDetails(userData);
455456
if (mainWindowOpen && window) {

src/elgato-cloud-window.cpp

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,77 @@ void Avatar::paintEvent(QPaintEvent *e)
8585
paint.end();
8686
}
8787

88+
AvatarImage::AvatarImage(QWidget* parent) : QWidget(parent)
89+
{
90+
auto api = MarketplaceApi::getInstance();
91+
setFixedWidth(40);
92+
setFixedHeight(40);
93+
94+
std::string imageBaseDir = GetDataPath();
95+
imageBaseDir += "/images/";
96+
97+
std::string imagePath = api->avatarReady()
98+
? api->avatarPath()
99+
: imageBaseDir + "image-loading.svg";
100+
101+
QPixmap avatarPixmap = _setupImage(imagePath);
102+
103+
auto layout = new QVBoxLayout(this);
104+
layout->setContentsMargins(1, 1, 1, 1);
105+
layout->setSpacing(0);
106+
_avatarImg = new QLabel(this);
107+
_avatarImg->setPixmap(avatarPixmap);
108+
_avatarImg->setSizePolicy(QSizePolicy::Preferred,
109+
QSizePolicy::Preferred);
110+
layout->addWidget(_avatarImg);
111+
connect(api, &MarketplaceApi::AvatarDownloaded, this, [this]() {
112+
update();
113+
});
114+
}
115+
116+
void AvatarImage::update()
117+
{
118+
auto api = MarketplaceApi::getInstance();
119+
std::string imageBaseDir = GetDataPath();
120+
imageBaseDir += "/images/";
121+
122+
std::string imagePath = api->avatarReady()
123+
? api->avatarPath()
124+
: imageBaseDir + "image-loading.svg";
125+
QPixmap avatarPixmap = _setupImage(imagePath);
126+
127+
_avatarImg->setPixmap(avatarPixmap);
128+
}
129+
130+
QPixmap AvatarImage::_setupImage(std::string imagePath)
131+
{
132+
int targetHeight = 38;
133+
int cornerRadius = 19;
134+
QPixmap img;
135+
136+
if (imagePath != "")
137+
img.load(imagePath.c_str());
138+
139+
int width = img.width();
140+
int height = img.height();
141+
int targetWidth =
142+
int((double)targetHeight * (double)width / (double)height);
143+
QPixmap target(targetWidth, targetHeight);
144+
target.fill(Qt::transparent);
145+
QPainter painter(&target);
146+
147+
painter.setRenderHint(QPainter::Antialiasing, true);
148+
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
149+
150+
QPainterPath path = QPainterPath();
151+
path.addRoundedRect(0, 0, targetWidth, targetHeight, cornerRadius,
152+
cornerRadius);
153+
painter.setClipPath(path);
154+
painter.drawPixmap(0, 0, img.scaledToHeight(targetHeight));
155+
156+
return target;
157+
}
158+
88159
DownloadButton::DownloadButton(QWidget *parent) : QWidget(parent)
89160
{
90161
std::string imageBaseDir = GetDataPath();
@@ -307,7 +378,13 @@ WindowToolBar::WindowToolBar(QWidget *parent) : QWidget(parent)
307378

308379
_layout->addWidget(_logOutButton);
309380
_avatar = new Avatar(this);
381+
_avatarImage = new AvatarImage(this);
310382
_layout->addWidget(_avatar);
383+
_layout->addWidget(_avatarImage);
384+
385+
connect(api, &MarketplaceApi::AvatarDownloaded, this, [this]() {
386+
updateState();
387+
});
311388
}
312389

313390
WindowToolBar::~WindowToolBar() {}
@@ -319,11 +396,14 @@ void WindowToolBar::disableLogout(bool disabled)
319396

320397
void WindowToolBar::updateState()
321398
{
399+
auto api = MarketplaceApi::getInstance();
322400
_logInButton->setHidden(elgatoCloud->loggedIn);
323401
_logOutButton->setHidden(!elgatoCloud->loggedIn);
324-
_avatar->setHidden(!elgatoCloud->loggedIn);
402+
_avatar->setHidden(!elgatoCloud->loggedIn || api->avatarReady());
403+
_avatarImage->setHidden(!elgatoCloud->loggedIn || !api->avatarReady());
325404
if (elgatoCloud->loggedIn) {
326405
_avatar->update();
406+
_avatarImage->update();
327407
}
328408
}
329409

0 commit comments

Comments
 (0)