Browse Source

Update plugin manager to 0.6 API

tags/v0.6.0
Andrew Belt 7 years ago
parent
commit
f9087788bc
4 changed files with 196 additions and 226 deletions
  1. +85
    -89
      src/app/PluginManagerWidget.cpp
  2. +2
    -2
      src/app/app.cpp
  3. +107
    -134
      src/plugin.cpp
  4. +2
    -1
      src/util/request.cpp

+ 85
- 89
src/app/PluginManagerWidget.cpp View File

@@ -8,12 +8,53 @@
namespace rack {


struct RegisterButton : Button {
void onAction(EventAction &e) override {
std::thread t([&]() {
systemOpenBrowser("https://vcvrack.com/");
});
t.detach();
}
};


struct LogInButton : Button {
TextField *emailField;
TextField *passwordField;
void onAction(EventAction &e) override {
std::thread t(pluginLogIn, emailField->text, passwordField->text);
t.detach();
passwordField->text = "";
}
};


struct StatusLabel : Label {
void step() override {
text = pluginGetLoginStatus();
}
};


struct ManageButton : Button {
void onAction(EventAction &e) override {
std::thread t([&]() {
systemOpenBrowser("https://vcvrack.com/");
});
t.detach();
}
};


struct SyncButton : Button {
bool checked = false;
/** Updates are available */
bool available = false;
/** Plugins have been updated */
bool completed = false;

void step() override {
// Check for plugin update on first step()
if (!checked) {
std::thread t([this]() {
if (pluginSync(true))
@@ -22,6 +63,7 @@ struct SyncButton : Button {
t.detach();
checked = true;
}
// Display message if we've completed updates
if (completed) {
if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been updated. Close Rack and re-launch it to load new updates.")) {
windowClose();
@@ -52,112 +94,83 @@ struct SyncButton : Button {
};


struct LogOutButton : Button {
void onAction(EventAction &e) override {
pluginLogOut();
}
};


struct DownloadProgressBar : ProgressBar {
void step() override {
label = "Downloading";
std::string name = pluginGetDownloadName();
if (name != "")
label += " " + name;
setValue(100.0 * pluginGetDownloadProgress());
}
};


struct CancelButton : Button {
void onAction(EventAction &e) override {
pluginCancelDownload();
}
};


PluginManagerWidget::PluginManagerWidget() {
box.size.y = BND_WIDGET_HEIGHT;
float margin = 5;

{
loginWidget = new Widget();
Vec pos = Vec(0, 0);

struct RegisterButton : Button {
void onAction(EventAction &e) override {
std::thread t([&]() {
systemOpenBrowser("https://vcvrack.com/");
});
t.detach();
}
};
SequentialLayout *layout = Widget::create<SequentialLayout>(Vec(0, 0));
layout->spacing = 5;
loginWidget = layout;

Button *registerButton = new RegisterButton();
registerButton->box.pos = pos;
registerButton->box.size.x = 75;
registerButton->text = "Register";
loginWidget->addChild(registerButton);
pos.x += registerButton->box.size.x;

pos.x += margin;
TextField *emailField = new TextField();
emailField->box.pos = pos;
emailField->box.size.x = 175;
emailField->placeholder = "Email";
loginWidget->addChild(emailField);
pos.x += emailField->box.size.x;

pos.x += margin;
PasswordField *passwordField = new PasswordField();
passwordField->box.pos = pos;
passwordField->box.size.x = 175;
passwordField->placeholder = "Password";
loginWidget->addChild(passwordField);
pos.x += passwordField->box.size.x;

struct LogInButton : Button {
TextField *emailField;
TextField *passwordField;
void onAction(EventAction &e) override {
std::thread t(pluginLogIn, emailField->text, passwordField->text);
t.detach();
passwordField->text = "";
}
};
pos.x += margin;

LogInButton *logInButton = new LogInButton();
logInButton->box.pos = pos;
logInButton->box.size.x = 100;
logInButton->text = "Log in";
logInButton->emailField = emailField;
logInButton->passwordField = passwordField;
loginWidget->addChild(logInButton);
pos.x += logInButton->box.size.x;

struct StatusLabel : Label {
void step() override {
text = pluginGetLoginStatus();
}
};
Label *label = new StatusLabel();
label->box.pos = pos;
loginWidget->addChild(label);

addChild(loginWidget);
}

{
manageWidget = new Widget();
Vec pos = Vec(0, 0);

struct ManageButton : Button {
void onAction(EventAction &e) override {
std::thread t([&]() {
systemOpenBrowser("https://vcvrack.com/");
});
t.detach();
}
};
SequentialLayout *layout = Widget::create<SequentialLayout>(Vec(0, 0));
layout->spacing = 5;
manageWidget = layout;

Button *manageButton = new ManageButton();
manageButton->box.pos = pos;
manageButton->box.size.x = 125;
manageButton->text = "Manage plugins";
manageWidget->addChild(manageButton);
pos.x += manageButton->box.size.x;

pos.x += margin;
Button *syncButton = new SyncButton();
syncButton->box.pos = pos;
syncButton->box.size.x = 125;
syncButton->text = "Update plugins";
manageWidget->addChild(syncButton);
pos.x += syncButton->box.size.x;

struct LogOutButton : Button {
void onAction(EventAction &e) override {
pluginLogOut();
}
};
pos.x += margin;
Button *logOutButton = new LogOutButton();
logOutButton->box.pos = pos;
logOutButton->box.size.x = 100;
logOutButton->text = "Log out";
manageWidget->addChild(logOutButton);
@@ -166,37 +179,20 @@ PluginManagerWidget::PluginManagerWidget() {
}

{
downloadWidget = new Widget();
Vec pos = Vec(0, 0);

struct DownloadProgressBar : ProgressBar {
void step() override {
label = "Downloading";
std::string name = pluginGetDownloadName();
if (name != "")
label += " " + name;
setValue(100.0 * pluginGetDownloadProgress());
}
};
SequentialLayout *layout = Widget::create<SequentialLayout>(Vec(0, 0));
layout->spacing = 5;
downloadWidget = layout;

ProgressBar *downloadProgress = new DownloadProgressBar();
downloadProgress->box.pos = pos;
downloadProgress->box.size.x = 300;
downloadProgress->setLimits(0, 100);
downloadProgress->unit = "%";
downloadWidget->addChild(downloadProgress);
pos.x += downloadProgress->box.size.x;

// struct CancelButton : Button {
// void onAction(EventAction &e) override {
// pluginCancelDownload();
// }
// };
// pos.x += margin;
// Button *logOutButton = new CancelButton();
// logOutButton->box.pos = pos;
// logOutButton->box.size.x = 100;
// logOutButton->text = "Cancel";
// downloadWidget->addChild(logOutButton);

// Button *cancelButton = new CancelButton();
// cancelButton->box.size.x = 100;
// cancelButton->text = "Cancel";
// downloadWidget->addChild(cancelButton);

addChild(downloadWidget);
}


+ 2
- 2
src/app/app.cpp View File

@@ -5,8 +5,8 @@ namespace rack {

std::string gApplicationName = "VCV Rack";
std::string gApplicationVersion = TOSTRING(VERSION);
std::string gApiHost = "https://api.vcvrack.com";
// std::string gApiHost = "http://localhost:8081";
// std::string gApiHost = "https://api.vcvrack.com";
std::string gApiHost = "http://localhost:8081";

RackWidget *gRackWidget = NULL;
Toolbar *gToolbar = NULL;


+ 107
- 134
src/plugin.cpp View File

@@ -126,98 +126,77 @@ static bool loadPlugin(std::string path) {
return true;
}

static bool syncPlugin(json_t *pluginJ, bool dryRun) {
json_t *slugJ = json_object_get(pluginJ, "slug");
if (!slugJ)
static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) {
// Get latest version
json_t *latestVersionJ = json_object_get(manifestJ, "latestVersion");
if (!latestVersionJ) {
warn("Could not get latest version of plugin %s", slug.c_str());
return false;
std::string slug = json_string_value(slugJ);

// Get community version
std::string version;
json_t *versionJ = json_object_get(pluginJ, "version");
if (versionJ) {
version = json_string_value(versionJ);
}
std::string latestVersion = json_string_value(latestVersionJ);

// Check whether we already have a plugin with the same slug and version
for (Plugin *plugin : gPlugins) {
if (plugin->slug == slug) {
// plugin->version might be blank, so adding a version of the manifest will update the plugin
if (plugin->version == version)
return false;
}
}

json_t *nameJ = json_object_get(pluginJ, "name");
if (!nameJ)
Plugin *plugin = pluginGetPlugin(slug);
if (plugin && plugin->version == latestVersion) {
return false;
std::string name = json_string_value(nameJ);
}

std::string download;
std::string sha256;
json_t *nameJ = json_object_get(manifestJ, "name");
std::string name;
if (nameJ) {
name = json_string_value(nameJ);
}
else {
name = slug;
}

json_t *downloadsJ = json_object_get(pluginJ, "downloads");
if (downloadsJ) {
#if ARCH_WIN
#define DOWNLOADS_ARCH "win"
std::string arch = "win";
#elif ARCH_MAC
#define DOWNLOADS_ARCH "mac"
std::string arch = "mac";
#elif ARCH_LIN
#define DOWNLOADS_ARCH "lin"
std::string arch = "lin";
#endif
json_t *archJ = json_object_get(downloadsJ, DOWNLOADS_ARCH);
if (archJ) {
// Get download URL
json_t *downloadJ = json_object_get(archJ, "download");
if (downloadJ)
download = json_string_value(downloadJ);
// Get SHA256 hash
json_t *sha256J = json_object_get(archJ, "sha256");
if (sha256J)
sha256 = json_string_value(sha256J);
}
}

json_t *productIdJ = json_object_get(pluginJ, "productId");
if (productIdJ) {
download = gApiHost;
download += "/download";
download += "?slug=";
download += slug;
download += "&token=";
download += requestEscape(gToken);
}

if (download.empty()) {
warn("Could not get download URL for plugin %s", slug.c_str());
return false;
std::string downloadUrl;
downloadUrl = gApiHost;
downloadUrl += "/download";
if (dryRun) {
downloadUrl += "/available";
}

// If plugin is not loaded, download the zip file to /plugins
downloadName = name;
downloadProgress = 0.0;

// Download zip
std::string pluginsDir = assetLocal("plugins");
std::string pluginPath = pluginsDir + "/" + slug;
std::string zipPath = pluginPath + ".zip";
bool success = requestDownload(download, zipPath, &downloadProgress);
if (!success) {
warn("Plugin %s download was unsuccessful", slug.c_str());
return false;
downloadUrl += "?token=" + requestEscape(gToken);
downloadUrl += "&slug=" + requestEscape(slug);
downloadUrl += "&version=" + requestEscape(latestVersion);
downloadUrl += "&arch=" + requestEscape(arch);

if (dryRun) {
// Check if available
json_t *availableResJ = requestJson(METHOD_GET, downloadUrl, NULL);
if (!availableResJ) {
warn("Could not check whether download is available");
return false;
}
defer({
json_decref(availableResJ);
});
json_t *successJ = json_object_get(availableResJ, "success");
return json_boolean_value(successJ);
}
else {
downloadName = name;
downloadProgress = 0.0;
info("Downloading plugin %s %s %s", slug.c_str(), latestVersion.c_str(), arch.c_str());

if (!sha256.empty()) {
// Check SHA256 hash
std::string actualSha256 = requestSHA256File(zipPath);
if (actualSha256 != sha256) {
warn("Plugin %s does not match expected SHA256 checksum", slug.c_str());
// Download zip
std::string pluginDest = assetLocal("plugins/" + slug + ".zip");
if (!requestDownload(downloadUrl, pluginDest, &downloadProgress)) {
warn("Plugin %s download was unsuccessful", slug.c_str());
return false;
}
}

downloadName = "";
return true;
downloadName = "";
return true;
}
}

static void loadPlugins(std::string path) {
@@ -378,7 +357,6 @@ void pluginDestroy() {
}

bool pluginSync(bool dryRun) {
return false;
if (gToken.empty())
return false;

@@ -389,74 +367,69 @@ bool pluginSync(bool dryRun) {
downloadProgress = 0.0;
downloadName = "Updating plugins...";
}
defer({
isDownloading = false;
});

json_t *resJ = NULL;
json_t *communityResJ = NULL;
// Get user's plugins list
json_t *pluginsReqJ = json_object();
json_object_set(pluginsReqJ, "token", json_string(gToken.c_str()));
json_t *pluginsResJ = requestJson(METHOD_GET, gApiHost + "/plugins", pluginsReqJ);
json_decref(pluginsReqJ);
if (!pluginsResJ) {
warn("Request for user's plugins failed");
return false;
}
defer({
json_decref(pluginsResJ);
});

try {
// Download plugin slugs
json_t *reqJ = json_object();
json_object_set(reqJ, "version", json_string(gApplicationVersion.c_str()));
json_object_set(reqJ, "token", json_string(gToken.c_str()));
resJ = requestJson(METHOD_GET, gApiHost + "/plugins", reqJ);
json_decref(reqJ);
if (!resJ)
throw std::runtime_error("No response from server");
json_t *errorJ = json_object_get(pluginsResJ, "error");
if (errorJ) {
warn("Request for user's plugins returned an error: %s", json_string_value(errorJ));
return false;
}

json_t *errorJ = json_object_get(resJ, "error");
if (errorJ)
throw std::runtime_error(json_string_value(errorJ));

// Download community plugins
communityResJ = requestJson(METHOD_GET, gApiHost + "/community/plugins", NULL);
if (!communityResJ)
throw std::runtime_error("No response from server");

json_t *communityErrorJ = json_object_get(communityResJ, "error");
if (communityErrorJ)
throw std::runtime_error(json_string_value(communityErrorJ));

// Check each plugin in list of plugin slugs
json_t *pluginSlugsJ = json_object_get(resJ, "plugins");
json_t *communityPluginsJ = json_object_get(communityResJ, "plugins");

size_t index;
json_t *pluginSlugJ;
json_array_foreach(pluginSlugsJ, index, pluginSlugJ) {
std::string slug = json_string_value(pluginSlugJ);
// Search for plugin slug in community
size_t communityIndex;
json_t *communityPluginJ = NULL;
json_array_foreach(communityPluginsJ, communityIndex, communityPluginJ) {
json_t *communitySlugJ = json_object_get(communityPluginJ, "slug");
if (!communitySlugJ)
continue;
std::string communitySlug = json_string_value(communitySlugJ);
if (slug == communitySlug)
break;
}
// Get community manifests
json_t *manifestsResJ = requestJson(METHOD_GET, gApiHost + "/community/manifests", NULL);
if (!manifestsResJ) {
warn("Request for community manifests failed");
return false;
}
defer({
json_decref(manifestsResJ);
});

// Sync plugin
if (syncPlugin(communityPluginJ, dryRun)) {
available = true;
}
else {
warn("Plugin %s not found in community", slug.c_str());
}
}
// Check each plugin in list of plugin slugs
json_t *pluginsJ = json_object_get(pluginsResJ, "plugins");
if (!pluginsJ) {
warn("No plugins array");
return false;
}
catch (std::runtime_error &e) {
warn("Plugin sync error: %s", e.what());
json_t *manifestsJ = json_object_get(manifestsResJ, "manifests");
if (!manifestsJ) {
warn("No manifests object");
return false;
}

if (communityResJ)
json_decref(communityResJ);
size_t slugIndex;
json_t *slugJ;
json_array_foreach(pluginsJ, slugIndex, slugJ) {
std::string slug = json_string_value(slugJ);
// Search for slug in manifests
const char *manifestSlug;
json_t *manifestJ = NULL;
json_object_foreach(manifestsJ, manifestSlug, manifestJ) {
if (slug == std::string(manifestSlug))
break;
}

if (resJ)
json_decref(resJ);
if (!manifestJ)
continue;

if (!dryRun) {
isDownloading = false;
if (syncPlugin(slug, manifestJ, dryRun)) {
available = true;
}
}

return available;


+ 2
- 1
src/util/request.cpp View File

@@ -131,8 +131,9 @@ bool requestDownload(std::string url, std::string filename, float *progress) {
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, progress);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
// Fail on 4xx and 5xx HTTP codes
curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);

info("Downloading %s", url.c_str());
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);



Loading…
Cancel
Save