Added update check functionality. Finally.

This commit is contained in:
casey langen 2017-06-28 00:51:02 -07:00
parent 21cb6e8820
commit bdb62b405a
16 changed files with 339 additions and 5 deletions

View File

@ -17,6 +17,7 @@ mkdir -p "$VANILLA/plugins"
mkdir -p "$VANILLA/themes"
mkdir -p "$VANILLA/locales"
cp bin/release/musikbox.exe "$VANILLA"
cp bin/release/*.dll "$VANILLA"
cp bin/release/plugins/*.dll "$VANILLA/plugins"
cp bin/release/themes/*.json "$VANILLA/themes"
cp bin/release/locales/*.json "$VANILLA/locales"
@ -30,6 +31,7 @@ mkdir -p "$MILKDROP/plugins"
mkdir -p "$MILKDROP/themes"
mkdir -p "$MILKDROP/locales"
cp bin/release/musikbox.exe "$MILKDROP"
cp bin/release/*.dll "$MILKDROP"
cp bin/release/plugins/*.dll "$MILKDROP/plugins"
cp bin/release/themes/*.json "$MILKDROP/themes"
cp bin/release/locales/*.json "$MILKDROP/locales"

View File

@ -137,6 +137,21 @@ std::string musik::core::GetDataDirectory(bool create) {
return directory;
}
inline void silentDelete(const std::string fn) {
boost::system::error_code ec;
boost::filesystem::remove(boost::filesystem::path(fn), ec);
}
void musik::core::RemoveOldDlls() {
#ifdef WIN32
std::string path = GetPluginDirectory();
silentDelete(path + "libcurl.dll");
silentDelete(path + "crypto-41.dll");
silentDelete(path + "ssl-43.dll");
silentDelete(path + "tls-15.dll");
#endif
}
void musik::core::MigrateOldDataDirectory() {
std::string oldDirectory =

View File

@ -50,5 +50,6 @@ namespace musik { namespace core {
/* renames ~/.mC2 -> ~/.musikcube */
void MigrateOldDataDirectory();
void RemoveOldDlls();
} }

View File

@ -21,6 +21,7 @@ set (BOX_SRCS
./app/util/GlobalHotkeys.cpp
./app/util/PreferenceKeys.cpp
./app/util/Playback.cpp
./app/util/UpdateCheck.cpp
./app/window/CategoryListView.cpp
./app/window/LogWindow.cpp
./app/window/TrackListView.cpp

View File

@ -106,6 +106,7 @@ int main(int argc, char* argv[]) {
musik::core::i18n::Locale::Instance().Initialize(musik::core::GetApplicationDirectory() + "/locales/");
#ifdef WIN32
musik::core::RemoveOldDlls();
AddDllDirectory(u8to16(musik::core::GetPluginDirectory()).c_str());
#endif

View File

@ -41,6 +41,7 @@
#include <app/util/Messages.h>
#include <app/util/PreferenceKeys.h>
#include <app/util/UpdateCheck.h>
#include "SettingsLayout.h"
#include "MainLayout.h"
@ -51,6 +52,8 @@ using namespace musik::core;
using namespace musik::core::runtime;
using namespace cursespp;
static UpdateCheck updateCheck;
static void updateSyncingText(TextLabel* label, int updates) {
try {
if (updates <= 0) {
@ -82,9 +85,12 @@ MainLayout::MainLayout(ILibraryPtr library)
library->Indexer()->Started.connect(this, &MainLayout::OnIndexerStarted);
library->Indexer()->Finished.connect(this, &MainLayout::OnIndexerFinished);
library->Indexer()->Progress.connect(this, &MainLayout::OnIndexerProgress);
this->RunUpdateCheck();
}
MainLayout::~MainLayout() {
updateCheck.Cancel();
}
void MainLayout::ResizeToViewport() {
@ -283,3 +289,31 @@ void MainLayout::OnIndexerProgress(int count) {
void MainLayout::OnIndexerFinished(int count) {
this->PostMessage(message::IndexerFinished);
}
void MainLayout::RunUpdateCheck() {
updateCheck.Run([this](bool updateRequired, std::string version, std::string url) {
if (updateRequired) {
std::string prefKey = prefs::keys::LastAcknowledgedUpdateVersion;
std::string acknowledged = this->prefs->GetString(prefKey);
if (acknowledged != version) {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
std::string message = boost::str(boost::format(
_TSTR("update_check_dialog_message")) % version % url);
(*dialog)
.SetTitle(_TSTR("update_check_dialog_title"))
.SetMessage(message)
.AddButton(
"KEY_ENTER", "ENTER", _TSTR("button_dont_remind_me"),
[this, prefKey, version](std::string key) {
this->prefs->SetString(prefKey.c_str(), version.c_str());
this->prefs->Save();
})
.AddButton("^[", "ESC", _TSTR("button_remind_me_later"));
App::Overlays().Push(dialog);
}
}
});
}

View File

@ -78,8 +78,9 @@ namespace musik {
void OnIndexerProgress(int count);
void OnIndexerFinished(int count);
void Initialize();
void RunUpdateCheck();
cursespp::IWindowPtr BlurShortcuts();
void FocusShortcuts();

View File

@ -63,6 +63,8 @@ namespace musik {
static const int TracksAddedToPlaylist = 1031;
static const int PlaylistCreated = 1032;
static const int UpdateCheckFinished = 1033;
}
}
}

View File

@ -43,6 +43,7 @@ namespace musik { namespace box { namespace prefs {
const std::string keys::ColorTheme = "ColorTheme";
const std::string keys::MinimizeToTray = "MinimizeToTray";
const std::string keys::StartMinimized = "StartMinimized";
const std::string keys::LastAcknowledgedUpdateVersion = "LastAcknowledgedUpdateVersion";
} } }

View File

@ -45,6 +45,7 @@ namespace musik { namespace box { namespace prefs {
extern const std::string ColorTheme;
extern const std::string MinimizeToTray;
extern const std::string StartMinimized;
extern const std::string LastAcknowledgedUpdateVersion;
}
} } }

View File

@ -0,0 +1,192 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "UpdateCheck.h"
#include <json.hpp>
#include <cursespp/Window.h>
#include <app/util/Messages.h>
#include <app/util/Version.h>
#include <core/runtime/Message.h>
using namespace nlohmann;
using namespace musik::box;
using namespace musik::core::runtime;
static const std::string UPDATE_CHECK_URL = "https://musikcube.com/version";
static const std::string LATEST = "latest";
static const std::string MAJOR = "major";
static const std::string MINOR = "minor";
static const std::string PATCH = "patch";
static const std::string URL = "url";
#ifdef WIN32
static const std::string PLATFORM = "win32";
#elif defined __APPLE__
static const std::string PLATFORM = "macos";
#else
static const std::string PLATFORM = "linux";
#endif
static inline int64_t versionCode(short major, short minor, short patch) {
int64_t version = major;
version = (version << 16) | minor;
version = (version << 16) | patch;
return version;
}
static inline std::string formattedVersion(short major, short minor, short patch) {
return boost::str(boost::format("%d.%d.%d") % major % minor % patch);
}
size_t UpdateCheck::curlWriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
if (ptr && userdata) {
UpdateCheck* context = static_cast<UpdateCheck*>(userdata);
if (context->cancel) {
return 0; /* aborts */
}
context->result += std::string(ptr, size * nmemb);
}
return size * nmemb;
}
UpdateCheck::UpdateCheck() {
this->curl = nullptr;
}
bool UpdateCheck::Run(Callback callback) {
std::unique_lock<std::recursive_mutex> lock(this->mutex);
if (this->thread || !callback) {
return false;
}
this->Reset();
this->callback = callback;
this->curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, UPDATE_CHECK_URL.c_str());
curl_easy_setopt(curl, CURLOPT_HEADER, 0);
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "musikcube UpdateCheck");
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlWriteCallback);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3000);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 7500);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 500);
this->thread.reset(new std::thread([this] {
bool needsUpdate = false;
if (curl_easy_perform(this->curl) == CURLE_OK) {
try {
json data = json::parse(this->result);
auto platform = data[LATEST][PLATFORM];
short major = platform[MAJOR];
short minor = platform[MINOR];
short patch = platform[PATCH];
this->updateUrl = platform[URL];
this->latestVersion = formattedVersion(major, minor, patch);
int64_t current = versionCode(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
int64_t latest = versionCode(major, minor, patch);
needsUpdate = latest > current;
}
catch (...) {
/* malformed. nothing we can do. */
}
}
cursespp::Window::MessageQueue().Post(
Message::Create(this, message::UpdateCheckFinished, needsUpdate));
}));
return true;
}
void UpdateCheck::Cancel() {
std::unique_lock<std::recursive_mutex> lock(this->mutex);
if (this->thread) {
this->cancel = true;
if (this->thread->joinable()) {
this->thread->join();
}
this->Reset();
}
}
void UpdateCheck::Reset() {
std::unique_lock<std::recursive_mutex> lock(this->mutex);
if (this->curl) {
curl_easy_cleanup(this->curl);
this->curl = nullptr;
}
this->cancel = false;
this->callback = Callback();
this->result = "";
if (this->thread && this->thread->joinable()) {
this->thread->detach();
}
this->thread.reset();
}
void UpdateCheck::ProcessMessage(IMessage &message) {
if (message.Type() == message::UpdateCheckFinished) {
auto callback = this->callback;
this->Reset();
if (callback) {
bool updateRequired = message.UserData1() != 0;
callback(updateRequired, this->latestVersion, this->updateUrl);
}
}
}

View File

@ -0,0 +1,66 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include <curl/curl.h>
#include <thread>
#include <mutex>
#include <core/runtime/IMessageTarget.h>
namespace musik { namespace box {
class UpdateCheck : private musik::core::runtime::IMessageTarget {
public:
/* args = updateRequired, version, url */
using Callback = std::function<void(bool, std::string, std::string)>;
UpdateCheck();
bool Run(Callback callback);
void Cancel();
private:
void Reset();
static size_t curlWriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata);
virtual void ProcessMessage(musik::core::runtime::IMessage &message);
std::recursive_mutex mutex;
std::shared_ptr<std::thread> thread;
Callback callback;
std::string result, latestVersion, updateUrl;
bool cancel;
CURL* curl;
};
} }

View File

@ -1,3 +1,6 @@
#pragma once
#define VERSION_MAJOR 0
#define VERSION_MINOR 18
#define VERSION_PATCH 0
#define VERSION "0.18.0"

View File

@ -8,6 +8,9 @@
"button_no": "no",
"button_save": "save",
"button_cancel": "cancel",
"button_close": "close",
"button_dont_remind_me": "don't remind me again",
"button_remind_me_later": "remind me later",
"browse_title_artists": "artists",
"browse_title_albums": "albums",
@ -118,7 +121,10 @@
"tracklist_unknown_album": "[unknown album]",
"main_syncing_banner_start": "syncing metadata...",
"main_syncing_banner": "syncing metadata (%d tracks processed)"
"main_syncing_banner": "syncing metadata (%d tracks processed)",
"update_check_dialog_title": "new version available!",
"update_check_dialog_message": "musikbox version '%s' is now available for download. a changelog and binaries are available at:\n\n%s"
},
"dimensions": {

View File

@ -89,7 +89,7 @@
xcopy "$(ProjectDir)data\themes\*" "$(TargetDir)themes\" /Y /e
if not exist "$(TargetDir)locales" mkdir "$(TargetDir)locales"
xcopy "$(ProjectDir)data\locales\*" "$(TargetDir)locales\" /Y /e
xcopy "$(SolutionDir)src\3rdparty\win32_bin\*" "$(TargetDir)plugins" /Y /e
xcopy "$(SolutionDir)src\3rdparty\win32_bin\*" "$(TargetDir)" /Y /e
xcopy "$(SolutionDir)src\plugins\websocket_remote\3rdparty\win32_bin\$(Configuration)\*" "$(TargetDir)plugins" /Y /e</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
@ -130,7 +130,7 @@ xcopy "$(SolutionDir)src\plugins\websocket_remote\3rdparty\win32_bin\$(Configura
xcopy "$(ProjectDir)data\themes\*" "$(TargetDir)themes\" /Y /e
if not exist "$(TargetDir)locales" mkdir "$(TargetDir)locales"
xcopy "$(ProjectDir)data\locales\*" "$(TargetDir)locales\" /Y /e
xcopy "$(SolutionDir)src\3rdparty\win32_bin\*" "$(TargetDir)plugins" /Y /e
xcopy "$(SolutionDir)src\3rdparty\win32_bin\*" "$(TargetDir)" /Y /e
xcopy "$(SolutionDir)src\plugins\websocket_remote\3rdparty\win32_bin\$(Configuration)\*" "$(TargetDir)plugins" /Y /e</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
@ -155,6 +155,7 @@ xcopy "$(SolutionDir)src\plugins\websocket_remote\3rdparty\win32_bin\$(Configura
<ClCompile Include="app\util\Hotkeys.cpp" />
<ClCompile Include="app\util\Playback.cpp" />
<ClCompile Include="app\util\PreferenceKeys.cpp" />
<ClCompile Include="app\util\UpdateCheck.cpp" />
<ClCompile Include="app\window\CategoryListView.cpp" />
<ClCompile Include="app\window\LogWindow.cpp" />
<ClCompile Include="app\window\TrackListView.cpp" />
@ -209,6 +210,7 @@ xcopy "$(SolutionDir)src\plugins\websocket_remote\3rdparty\win32_bin\$(Configura
<ClInclude Include="app\util\Messages.h" />
<ClInclude Include="app\util\Playback.h" />
<ClInclude Include="app\util\PreferenceKeys.h" />
<ClInclude Include="app\util\UpdateCheck.h" />
<ClInclude Include="app\util\Version.h" />
<ClInclude Include="app\window\CategoryListView.h" />
<ClInclude Include="app\window\LogWindow.h" />

View File

@ -138,6 +138,9 @@
<ClCompile Include="app\overlay\ServerOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
<ClCompile Include="app\util\UpdateCheck.cpp">
<Filter>app\util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -325,6 +328,9 @@
<ClInclude Include="app\overlay\ServerOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
<ClInclude Include="app\util\UpdateCheck.h">
<Filter>app\util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">