Browse Source

Rename `updater::` to `library::`. Move VCV Library synchronization code from `plugin::` to `library::`.

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
155ae4ead6
8 changed files with 441 additions and 440 deletions
  1. +51
    -0
      include/library.hpp
  2. +1
    -23
      include/plugin.hpp
  3. +0
    -26
      include/updater.hpp
  4. +27
    -27
      src/app/MenuBar.cpp
  5. +359
    -0
      src/library.cpp
  6. +1
    -256
      src/plugin.cpp
  7. +0
    -106
      src/updater.cpp
  8. +2
    -2
      standalone/main.cpp

+ 51
- 0
include/library.hpp View File

@@ -0,0 +1,51 @@
#pragma once
#include <common.hpp>

#include <vector>


namespace rack {


/** Synchronizes plugins with the VCV Library and updates Rack itself
*/
namespace library {


struct Update {
std::string pluginSlug;
std::string pluginName;
std::string version;
std::string changelogUrl;
float progress = 0.f;
};


extern std::string version;
extern std::string changelogUrl;
extern float progress;

void init();
bool isLoggedIn();
void logIn(const std::string& email, const std::string& password);
void logOut();
bool isUpdateAvailable();
void queryUpdates();
bool hasUpdates();
void syncUpdate(Update* update);
void syncUpdates();
bool isSyncing();
/** Updates Rack automatically or opens the browser to the URL.
Blocking. Call on a detached thread.
*/
void update();


extern std::string loginStatus;
extern std::vector<Update> updates;
extern std::string updateStatus;
extern bool restartRequested;


} // namespace library
} // namespace rack

+ 1
- 23
include/plugin.hpp View File

@@ -9,30 +9,13 @@
namespace rack {


/** Plugin loader and VCV Library synchronizer
/** Loads and manages plugins
*/
namespace plugin {


struct Update {
std::string pluginSlug;
std::string pluginName;
std::string version;
std::string changelogUrl;
float progress = 0.f;
};


void init();
void destroy();
void logIn(const std::string& email, const std::string& password);
void logOut();
bool isLoggedIn();
void queryUpdates();
bool hasUpdates();
void syncUpdate(Update* update);
void syncUpdates();
bool isSyncing();
Plugin* getPlugin(const std::string& pluginSlug);
Model* getModel(const std::string& pluginSlug, const std::string& modelSlug);
/** Creates a Model from a JSON module object.
@@ -47,11 +30,6 @@ std::string normalizeSlug(const std::string& slug);

extern std::vector<Plugin*> plugins;

extern std::string loginStatus;
extern std::vector<Update> updates;
extern std::string updateStatus;
extern bool restartRequested;


} // namespace plugin
} // namespace rack

+ 0
- 26
include/updater.hpp View File

@@ -1,26 +0,0 @@
#pragma once
#include <common.hpp>


namespace rack {


/** Automatically updates the application.
*/
namespace updater {


extern std::string version;
extern std::string changelogUrl;
extern float progress;

void init();
bool isUpdateAvailable();
/** Updates Rack automatically or opens the browser to the URL.
Blocking. Call on a detached thread.
*/
void update();


} // namespace updater
} // namespace rack

+ 27
- 27
src/app/MenuBar.cpp View File

@@ -21,7 +21,7 @@
#include <system.hpp>
#include <plugin.hpp>
#include <patch.hpp>
#include <updater.hpp>
#include <library.hpp>


namespace rack {
@@ -608,7 +608,7 @@ struct LogInItem : ui::MenuItem {
std::string email = emailField->text;
std::string password = passwordField->text;
std::thread t([ = ] {
plugin::logIn(email, password);
library::logIn(email, password);
isLoggingIn = false;
});
t.detach();
@@ -618,7 +618,7 @@ struct LogInItem : ui::MenuItem {
void step() override {
disabled = isLoggingIn;
text = "Log in";
rightText = plugin::loginStatus;
rightText = library::loginStatus;
MenuItem::step();
}
};
@@ -626,13 +626,13 @@ struct LogInItem : ui::MenuItem {
struct SyncItem : ui::MenuItem {
void step() override {
disabled = true;
if (plugin::updateStatus != "") {
text = plugin::updateStatus;
if (library::updateStatus != "") {
text = library::updateStatus;
}
else if (plugin::isSyncing()) {
else if (library::isSyncing()) {
text = "Updating...";
}
else if (!plugin::hasUpdates()) {
else if (!library::hasUpdates()) {
text = "Up-to-date";
}
else {
@@ -644,7 +644,7 @@ struct SyncItem : ui::MenuItem {

void onAction(const event::Action& e) override {
std::thread t([ = ] {
plugin::syncUpdates();
library::syncUpdates();
});
t.detach();
e.consume(NULL);
@@ -652,9 +652,9 @@ struct SyncItem : ui::MenuItem {
};

struct PluginSyncItem : ui::MenuItem {
plugin::Update* update;
library::Update* update;

void setUpdate(plugin::Update* update) {
void setUpdate(library::Update* update) {
this->update = update;
text = update->pluginName;
plugin::Plugin* p = plugin::getPlugin(update->pluginSlug);
@@ -679,7 +679,7 @@ struct PluginSyncItem : ui::MenuItem {
}

void step() override {
disabled = plugin::isSyncing();
disabled = library::isSyncing();
if (update->progress >= 1) {
rightText = CHECKMARK_STRING;
disabled = true;
@@ -692,7 +692,7 @@ struct PluginSyncItem : ui::MenuItem {

void onAction(const event::Action& e) override {
std::thread t([ = ] {
plugin::syncUpdate(update);
library::syncUpdate(update);
});
t.detach();
e.consume(NULL);
@@ -701,7 +701,7 @@ struct PluginSyncItem : ui::MenuItem {

struct LogOutItem : ui::MenuItem {
void onAction(const event::Action& e) override {
plugin::logOut();
library::logOut();
}
};

@@ -714,7 +714,7 @@ struct LibraryMenu : ui::Menu {

void step() override {
// Refresh menu when appropriate
if (!loggedIn && plugin::isLoggedIn())
if (!loggedIn && library::isLoggedIn())
refresh();
Menu::step();
}
@@ -726,7 +726,7 @@ struct LibraryMenu : ui::Menu {
if (settings::devMode) {
addChild(createMenuLabel("Disabled in development mode"));
}
else if (!plugin::isLoggedIn()) {
else if (!library::isLoggedIn()) {
UrlItem* registerItem = new UrlItem;
registerItem->text = "Register VCV account";
registerItem->url = "https://vcvrack.com/";
@@ -765,14 +765,14 @@ struct LibraryMenu : ui::Menu {
syncItem->text = "Update all";
addChild(syncItem);

if (plugin::hasUpdates()) {
if (library::hasUpdates()) {
addChild(new ui::MenuSeparator);

ui::MenuLabel* updatesLabel = new ui::MenuLabel;
updatesLabel->text = "Updates";
addChild(updatesLabel);

for (plugin::Update& update : plugin::updates) {
for (library::Update& update : library::updates) {
PluginSyncItem* updateItem = new PluginSyncItem;
updateItem->setUpdate(&update);
addChild(updateItem);
@@ -799,11 +799,11 @@ struct LibraryButton : MenuButton {

void step() override {
notification->box.pos = math::Vec(0, 0);
notification->visible = plugin::hasUpdates();
notification->visible = library::hasUpdates();

// Popup when updates finish downloading
if (plugin::restartRequested) {
plugin::restartRequested = false;
if (library::restartRequested) {
library::restartRequested = false;
if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been downloaded. Close and re-launch Rack to load new updates.")) {
APP->window->close();
}
@@ -823,22 +823,22 @@ struct UpdateItem : ui::MenuItem {

UrlItem* changelogUrl = new UrlItem;
changelogUrl->text = "Changelog";
changelogUrl->url = updater::changelogUrl;
changelogUrl->url = library::changelogUrl;
menu->addChild(changelogUrl);

return menu;
}

void step() override {
if (updater::progress > 0) {
rightText = string::f("%.0f%%", updater::progress * 100.f);
if (library::progress > 0) {
rightText = string::f("%.0f%%", library::progress * 100.f);
}
MenuItem::step();
}

void onAction(const event::Action& e) override {
std::thread t([ = ] {
updater::update();
library::update();
});
t.detach();
e.consume(NULL);
@@ -859,10 +859,10 @@ struct HelpButton : MenuButton {
menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y));
menu->box.size.x = box.size.x;

if (updater::isUpdateAvailable()) {
if (library::isUpdateAvailable()) {
UpdateItem* updateItem = new UpdateItem;
updateItem->text = "Update " + APP_NAME;
updateItem->rightText = APP_VERSION + " → " + updater::version;
updateItem->rightText = APP_VERSION + " → " + library::version;
menu->addChild(updateItem);
}

@@ -885,7 +885,7 @@ struct HelpButton : MenuButton {

void step() override {
notification->box.pos = math::Vec(0, 0);
notification->visible = updater::isUpdateAvailable();
notification->visible = library::isUpdateAvailable();
MenuButton::step();
}
};


+ 359
- 0
src/library.cpp View File

@@ -0,0 +1,359 @@
#include <thread>

#include <library.hpp>
#include <settings.hpp>
#include <app/common.hpp>
#include <network.hpp>
#include <system.hpp>
#include <context.hpp>
#include <window.hpp>
#include <asset.hpp>
#include <settings.hpp>
#include <plugin.hpp>


namespace rack {
namespace library {


std::string version;
std::string changelogUrl;
float progress = 0.f;
static std::string downloadUrl;


static void checkVersion() {
std::string versionUrl = API_URL + "/version";
json_t* resJ = network::requestJson(network::METHOD_GET, versionUrl, NULL);
if (!resJ) {
WARN("Request for version failed");
return;
}
DEFER({json_decref(resJ);});

json_t* versionJ = json_object_get(resJ, "version");
if (versionJ)
version = json_string_value(versionJ);

json_t* changelogUrlJ = json_object_get(resJ, "changelogUrl");
if (changelogUrlJ)
changelogUrl = json_string_value(changelogUrlJ);

json_t* downloadUrlsJ = json_object_get(resJ, "downloadUrls");
if (downloadUrlsJ) {
json_t* downloadUrlJ = json_object_get(downloadUrlsJ, APP_ARCH.c_str());
if (downloadUrlJ)
downloadUrl = json_string_value(downloadUrlJ);
}
}


void init() {
if (settings::devMode)
return;

std::thread t([]() {
checkVersion();
});
t.detach();

// Sync in a detached thread
if (!settings::devMode) {
std::thread t([] {
queryUpdates();
});
t.detach();
}
}


bool isLoggedIn() {
return settings::token != "";
}


void logIn(const std::string& email, const std::string& password) {
loginStatus = "Logging in...";
json_t* reqJ = json_object();
json_object_set(reqJ, "email", json_string(email.c_str()));
json_object_set(reqJ, "password", json_string(password.c_str()));
std::string url = API_URL + "/token";
json_t* resJ = network::requestJson(network::METHOD_POST, url, reqJ);
json_decref(reqJ);

if (!resJ) {
loginStatus = "No response from server";
return;
}
DEFER({
json_decref(resJ);
});

json_t* errorJ = json_object_get(resJ, "error");
if (errorJ) {
const char* errorStr = json_string_value(errorJ);
loginStatus = errorStr;
return;
}

json_t* tokenJ = json_object_get(resJ, "token");
if (!tokenJ) {
loginStatus = "No token in response";
return;
}

const char* tokenStr = json_string_value(tokenJ);
settings::token = tokenStr;
loginStatus = "";
queryUpdates();
}


void logOut() {
settings::token = "";
updates.clear();
}


void update() {
if (downloadUrl == "")
return;

// Download update
// HACK getFilename is only supposed to be used for filesystem paths, not URLs.
std::string filename = system::getFilename(network::urlPath(downloadUrl));
std::string path = asset::user(filename);
INFO("Downloading update %s to %s", downloadUrl.c_str(), path.c_str());
network::requestDownload(downloadUrl, path, &progress);

#if defined ARCH_WIN
// Launch the installer
INFO("Launching update %s", path.c_str());
system::runProcessDetached(path);
#elif defined ARCH_MAC
std::string cmd;
// std::string appPath = system::join(asset::userDir, "Rack.app");
// cmd = "rm -rf '" + appPath + "'";
// std::system(cmd.c_str());
// // Unzip app using Apple's unzipper, since Rack's unzipper doesn't handle the metadata stuff correctly.
// cmd = "unzip -q '" + path + "' -d '" + asset::userDir + "'";
// std::system(cmd.c_str());
// // Open app in Finder
// cmd = "open -R '" + appPath + "'";
// std::system(cmd.c_str());

// Open Archive Utility
cmd = "open '" + path + "'";
std::system(cmd.c_str());
#elif defined ARCH_LIN
system::openFolder(asset::user(""));
#endif

APP->window->close();
}


bool isUpdateAvailable() {
return (version != "") && (version != APP_VERSION);
}


void queryUpdates() {
if (settings::token.empty())
return;

updates.clear();
updateStatus = "Querying for updates...";

// Get user's plugins list
std::string pluginsUrl = API_URL + "/plugins";
network::CookieMap cookies;
cookies["token"] = settings::token;
json_t* pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, NULL, cookies);
if (!pluginsResJ) {
WARN("Request for user's plugins failed");
updateStatus = "Could not query updates";
return;
}
DEFER({
json_decref(pluginsResJ);
});

json_t* errorJ = json_object_get(pluginsResJ, "error");
if (errorJ) {
WARN("Request for user's plugins returned an error: %s", json_string_value(errorJ));
updateStatus = "Could not query updates";
return;
}

// Get library manifests
std::string manifestsUrl = API_URL + "/library/manifests";
json_t* manifestsReq = json_object();
json_object_set(manifestsReq, "version", json_string(API_VERSION.c_str()));
json_t* manifestsResJ = network::requestJson(network::METHOD_GET, manifestsUrl, manifestsReq);
json_decref(manifestsReq);
if (!manifestsResJ) {
WARN("Request for library manifests failed");
updateStatus = "Could not query updates";
return;
}
DEFER({
json_decref(manifestsResJ);
});

json_t* manifestsJ = json_object_get(manifestsResJ, "manifests");
json_t* pluginsJ = json_object_get(pluginsResJ, "plugins");

size_t pluginIndex;
json_t* pluginJ;
json_array_foreach(pluginsJ, pluginIndex, pluginJ) {
Update update;
// Get plugin manifest
update.pluginSlug = json_string_value(pluginJ);
json_t* manifestJ = json_object_get(manifestsJ, update.pluginSlug.c_str());
if (!manifestJ) {
WARN("VCV account has plugin %s but no manifest was found", update.pluginSlug.c_str());
continue;
}

// Get plugin name
json_t* nameJ = json_object_get(manifestJ, "name");
if (nameJ)
update.pluginName = json_string_value(nameJ);

// Get version
json_t* versionJ = json_object_get(manifestJ, "version");
if (!versionJ) {
WARN("Plugin %s has no version in manifest", update.pluginSlug.c_str());
continue;
}
update.version = json_string_value(versionJ);

// Check if update is needed
plugin::Plugin* p = plugin::getPlugin(update.pluginSlug);
if (p && p->version == update.version)
continue;

// Check status
json_t* statusJ = json_object_get(manifestJ, "status");
if (!statusJ)
continue;
std::string status = json_string_value(statusJ);
if (status != "available")
continue;

// Get changelog URL
json_t* changelogUrlJ = json_object_get(manifestJ, "changelogUrl");
if (changelogUrlJ) {
update.changelogUrl = json_string_value(changelogUrlJ);
}

updates.push_back(update);
}


// Get module whitelist
{
std::string whitelistUrl = API_URL + "/moduleWhitelist";
json_t* whitelistResJ = network::requestJson(network::METHOD_GET, whitelistUrl, NULL, cookies);
if (!whitelistResJ) {
WARN("Request for module whitelist failed");
updateStatus = "Could not query updates";
return;
}
DEFER({
json_decref(whitelistResJ);
});

std::map<std::string, std::set<std::string>> moduleWhitelist;
json_t* pluginsJ = json_object_get(whitelistResJ, "plugins");

// Iterate plugins
const char* pluginSlug;
json_t* modulesJ;
json_object_foreach(pluginsJ, pluginSlug, modulesJ) {
// Iterate modules in plugin
size_t moduleIndex;
json_t* moduleSlugJ;
json_array_foreach(modulesJ, moduleIndex, moduleSlugJ) {
std::string moduleSlug = json_string_value(moduleSlugJ);
// Insert module in whitelist
moduleWhitelist[pluginSlug].insert(moduleSlug);
}
}

settings::moduleWhitelist = moduleWhitelist;
}

updateStatus = "";
}


bool hasUpdates() {
for (Update& update : updates) {
if (update.progress < 1.f)
return true;
}
return false;
}


static bool isSyncingUpdate = false;
static bool isSyncingUpdates = false;


void syncUpdate(Update* update) {
isSyncingUpdate = true;
DEFER({
isSyncingUpdate = false;
});

std::string downloadUrl = API_URL + "/download";
downloadUrl += "?slug=" + network::encodeUrl(update->pluginSlug);
downloadUrl += "&version=" + network::encodeUrl(update->version);
downloadUrl += "&arch=" + network::encodeUrl(APP_ARCH);

network::CookieMap cookies;
cookies["token"] = settings::token;

INFO("Downloading plugin %s %s %s", update->pluginSlug.c_str(), update->version.c_str(), APP_ARCH.c_str());

// Download zip
std::string pluginDest = system::join(asset::pluginsPath, update->pluginSlug + ".zip");
if (!network::requestDownload(downloadUrl, pluginDest, &update->progress, cookies)) {
WARN("Plugin %s download was unsuccessful", update->pluginSlug.c_str());
return;
}
}


void syncUpdates() {
isSyncingUpdates = true;
DEFER({
isSyncingUpdates = false;
});

if (settings::token.empty())
return;

for (Update& update : updates) {
if (update.progress < 1.f)
syncUpdate(&update);
}
restartRequested = true;
}


bool isSyncing() {
return isSyncingUpdate || isSyncingUpdates;
}


std::string loginStatus;
std::vector<Update> updates;
std::string updateStatus;
bool restartRequested = false;


} // namespace library
} // namespace rack

+ 1
- 256
src/plugin.cpp View File

@@ -11,7 +11,7 @@
#include <windows.h>
#include <direct.h>
#else
#include <dlfcn.h>
#include <dlfcn.h> // for dlopen
#endif
#include <dirent.h>

@@ -20,14 +20,11 @@

#include <plugin.hpp>
#include <system.hpp>
#include <network.hpp>
#include <asset.hpp>
#include <string.hpp>
#include <context.hpp>
#include <app/common.hpp>
#include <plugin/callbacks.hpp>
#include <settings.hpp>
#include <engine/Module.hpp>


namespace rack {
@@ -232,14 +229,6 @@ void init() {
system::unarchiveToFolder(fundamentalSrc.c_str(), asset::pluginsPath.c_str());
loadPlugin(fundamentalDir);
}

// Sync in a detached thread
if (!settings::devMode) {
std::thread t([] {
queryUpdates();
});
t.detach();
}
}


@@ -262,245 +251,6 @@ void destroy() {
}


void logIn(const std::string& email, const std::string& password) {
loginStatus = "Logging in...";
json_t* reqJ = json_object();
json_object_set(reqJ, "email", json_string(email.c_str()));
json_object_set(reqJ, "password", json_string(password.c_str()));
std::string url = API_URL + "/token";
json_t* resJ = network::requestJson(network::METHOD_POST, url, reqJ);
json_decref(reqJ);

if (!resJ) {
loginStatus = "No response from server";
return;
}
DEFER({
json_decref(resJ);
});

json_t* errorJ = json_object_get(resJ, "error");
if (errorJ) {
const char* errorStr = json_string_value(errorJ);
loginStatus = errorStr;
return;
}

json_t* tokenJ = json_object_get(resJ, "token");
if (!tokenJ) {
loginStatus = "No token in response";
return;
}

const char* tokenStr = json_string_value(tokenJ);
settings::token = tokenStr;
loginStatus = "";
queryUpdates();
}


void logOut() {
settings::token = "";
updates.clear();
}


bool isLoggedIn() {
return settings::token != "";
}


void queryUpdates() {
if (settings::token.empty())
return;

updates.clear();
updateStatus = "Querying for updates...";

// Get user's plugins list
std::string pluginsUrl = API_URL + "/plugins";
network::CookieMap cookies;
cookies["token"] = settings::token;
json_t* pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, NULL, cookies);
if (!pluginsResJ) {
WARN("Request for user's plugins failed");
updateStatus = "Could not query updates";
return;
}
DEFER({
json_decref(pluginsResJ);
});

json_t* errorJ = json_object_get(pluginsResJ, "error");
if (errorJ) {
WARN("Request for user's plugins returned an error: %s", json_string_value(errorJ));
updateStatus = "Could not query updates";
return;
}

// Get library manifests
std::string manifestsUrl = API_URL + "/library/manifests";
json_t* manifestsReq = json_object();
json_object_set(manifestsReq, "version", json_string(API_VERSION.c_str()));
json_t* manifestsResJ = network::requestJson(network::METHOD_GET, manifestsUrl, manifestsReq);
json_decref(manifestsReq);
if (!manifestsResJ) {
WARN("Request for library manifests failed");
updateStatus = "Could not query updates";
return;
}
DEFER({
json_decref(manifestsResJ);
});

json_t* manifestsJ = json_object_get(manifestsResJ, "manifests");
json_t* pluginsJ = json_object_get(pluginsResJ, "plugins");

size_t pluginIndex;
json_t* pluginJ;
json_array_foreach(pluginsJ, pluginIndex, pluginJ) {
Update update;
// Get plugin manifest
update.pluginSlug = json_string_value(pluginJ);
json_t* manifestJ = json_object_get(manifestsJ, update.pluginSlug.c_str());
if (!manifestJ) {
WARN("VCV account has plugin %s but no manifest was found", update.pluginSlug.c_str());
continue;
}

// Get plugin name
json_t* nameJ = json_object_get(manifestJ, "name");
if (nameJ)
update.pluginName = json_string_value(nameJ);

// Get version
json_t* versionJ = json_object_get(manifestJ, "version");
if (!versionJ) {
WARN("Plugin %s has no version in manifest", update.pluginSlug.c_str());
continue;
}
update.version = json_string_value(versionJ);

// Check if update is needed
Plugin* p = getPlugin(update.pluginSlug);
if (p && p->version == update.version)
continue;

// Check status
json_t* statusJ = json_object_get(manifestJ, "status");
if (!statusJ)
continue;
std::string status = json_string_value(statusJ);
if (status != "available")
continue;

// Get changelog URL
json_t* changelogUrlJ = json_object_get(manifestJ, "changelogUrl");
if (changelogUrlJ) {
update.changelogUrl = json_string_value(changelogUrlJ);
}

updates.push_back(update);
}


// Get module whitelist
{
std::string whitelistUrl = API_URL + "/moduleWhitelist";
json_t* whitelistResJ = network::requestJson(network::METHOD_GET, whitelistUrl, NULL, cookies);
if (!whitelistResJ) {
WARN("Request for module whitelist failed");
updateStatus = "Could not query updates";
return;
}
DEFER({
json_decref(whitelistResJ);
});

std::map<std::string, std::set<std::string>> moduleWhitelist;
json_t* pluginsJ = json_object_get(whitelistResJ, "plugins");

// Iterate plugins
const char* pluginSlug;
json_t* modulesJ;
json_object_foreach(pluginsJ, pluginSlug, modulesJ) {
// Iterate modules in plugin
size_t moduleIndex;
json_t* moduleSlugJ;
json_array_foreach(modulesJ, moduleIndex, moduleSlugJ) {
std::string moduleSlug = json_string_value(moduleSlugJ);
// Insert module in whitelist
moduleWhitelist[pluginSlug].insert(moduleSlug);
}
}

settings::moduleWhitelist = moduleWhitelist;
}

updateStatus = "";
}


bool hasUpdates() {
for (Update& update : updates) {
if (update.progress < 1.f)
return true;
}
return false;
}


static bool isSyncingUpdate = false;
static bool isSyncingUpdates = false;


void syncUpdate(Update* update) {
isSyncingUpdate = true;
DEFER({
isSyncingUpdate = false;
});

std::string downloadUrl = API_URL + "/download";
downloadUrl += "?slug=" + network::encodeUrl(update->pluginSlug);
downloadUrl += "&version=" + network::encodeUrl(update->version);
downloadUrl += "&arch=" + network::encodeUrl(APP_ARCH);

network::CookieMap cookies;
cookies["token"] = settings::token;

INFO("Downloading plugin %s %s %s", update->pluginSlug.c_str(), update->version.c_str(), APP_ARCH.c_str());

// Download zip
std::string pluginDest = system::join(asset::pluginsPath, update->pluginSlug + ".zip");
if (!network::requestDownload(downloadUrl, pluginDest, &update->progress, cookies)) {
WARN("Plugin %s download was unsuccessful", update->pluginSlug.c_str());
return;
}
}


void syncUpdates() {
isSyncingUpdates = true;
DEFER({
isSyncingUpdates = false;
});

if (settings::token.empty())
return;

for (Update& update : updates) {
if (update.progress < 1.f)
syncUpdate(&update);
}
restartRequested = true;
}


bool isSyncing() {
return isSyncingUpdate || isSyncingUpdates;
}


Plugin* getPlugin(const std::string& pluginSlug) {
for (Plugin* plugin : plugins) {
if (plugin->slug == pluginSlug) {
@@ -566,11 +316,6 @@ std::string normalizeSlug(const std::string& slug) {

std::vector<Plugin*> plugins;

std::string loginStatus;
std::vector<Update> updates;
std::string updateStatus;
bool restartRequested = false;


} // namespace plugin
} // namespace rack

+ 0
- 106
src/updater.cpp View File

@@ -1,106 +0,0 @@
#include <thread>

#include <updater.hpp>
#include <settings.hpp>
#include <app/common.hpp>
#include <network.hpp>
#include <system.hpp>
#include <context.hpp>
#include <window.hpp>
#include <asset.hpp>


namespace rack {
namespace updater {


std::string version;
std::string changelogUrl;
float progress = 0.f;
static std::string downloadUrl;


static void checkVersion() {
std::string versionUrl = API_URL + "/version";
json_t* resJ = network::requestJson(network::METHOD_GET, versionUrl, NULL);
if (!resJ) {
WARN("Request for version failed");
return;
}
DEFER({
json_decref(resJ);
});

json_t* versionJ = json_object_get(resJ, "version");
if (versionJ)
version = json_string_value(versionJ);

json_t* changelogUrlJ = json_object_get(resJ, "changelogUrl");
if (changelogUrlJ)
changelogUrl = json_string_value(changelogUrlJ);

json_t* downloadUrlsJ = json_object_get(resJ, "downloadUrls");
if (downloadUrlsJ) {
json_t* downloadUrlJ = json_object_get(downloadUrlsJ, APP_ARCH.c_str());
if (downloadUrlJ)
downloadUrl = json_string_value(downloadUrlJ);
}
}


void init() {
if (settings::devMode)
return;

std::thread t([]() {
checkVersion();
});
t.detach();
}


void update() {
if (downloadUrl == "")
return;

// Download update
// HACK getFilename is only supposed to be used for filesystem paths, not URLs.
std::string filename = system::getFilename(network::urlPath(downloadUrl));
std::string path = asset::user(filename);
INFO("Downloading update %s to %s", downloadUrl.c_str(), path.c_str());
network::requestDownload(downloadUrl, path, &progress);

#if defined ARCH_WIN
// Launch the installer
INFO("Launching update %s", path.c_str());
system::runProcessDetached(path);
#elif defined ARCH_MAC
std::string cmd;
// std::string appPath = system::join(asset::userDir, "Rack.app");
// cmd = "rm -rf '" + appPath + "'";
// std::system(cmd.c_str());
// // Unzip app using Apple's unzipper, since Rack's unzipper doesn't handle the metadata stuff correctly.
// cmd = "unzip -q '" + path + "' -d '" + asset::userDir + "'";
// std::system(cmd.c_str());
// // Open app in Finder
// cmd = "open -R '" + appPath + "'";
// std::system(cmd.c_str());

// Open Archive Utility
cmd = "open '" + path + "'";
std::system(cmd.c_str());
#elif defined ARCH_LIN
system::openFolder(asset::user(""));
#endif

APP->window->close();
}


bool isUpdateAvailable() {
return (version != "") && (version != APP_VERSION);
}


} // namespace updater
} // namespace rack

+ 2
- 2
standalone/main.cpp View File

@@ -19,7 +19,7 @@
#include <ui.hpp>
#include <system.hpp>
#include <string.hpp>
#include <updater.hpp>
#include <library.hpp>
#include <network.hpp>

#include <osdialog.h>
@@ -168,7 +168,7 @@ int main(int argc, char* argv[]) {
keyboard::init();
gamepad::init();
plugin::init();
updater::init();
library::init();
if (!settings::headless) {
ui::init();
windowInit();


Loading…
Cancel
Save