reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit d077e7643e4710b27225b807305170f255f5614d
parent edb1a9c131391f9a5ca08eb2e713f2f2a42b261f
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Mon, 23 Nov 2015 21:14:25 -0500

implement background file downloading

Diffstat:
MTupfile | 7++++++-
Asrc/download.cpp | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/download.hpp | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.cpp | 10+++++++++-
4 files changed, 244 insertions(+), 2 deletions(-)

diff --git a/Tupfile b/Tupfile @@ -9,12 +9,17 @@ CXXFLAGS += -DWDL_NO_DEFINE_MINMAX LDFLAGS := -dynamiclib -framework Cocoa -framework Carbon WDL := vendor/WDL/WDL -SWELL := $(WDL)/swell +SWELL := $(WDL)/swell WDLSOURCE := $(SWELL)/swell.cpp $(SWELL)/swell-ini.cpp WDLSOURCE += $(SWELL)/swell-gdi.mm $(SWELL)/swell-kb.mm $(SWELL)/swell-menu.mm WDLSOURCE += $(SWELL)/swell-misc.mm $(SWELL)/swell-dlg.mm $(SWELL)/swell-wnd.mm +JNETLIB := $(WDL)/jnetlib +WDLSOURCE += $(JNETLIB)/util.cpp $(JNETLIB)/httpget.cpp +WDLSOURCE += $(JNETLIB)/connection.cpp $(JNETLIB)/asyncdns.cpp + + !build = |> $(CXX) $(CXXFLAGS) -c %f -o %o |> !link = |> $(CXX) $(CXXFLAGS) %f $(LDFLAGS) -o %o |> diff --git a/src/download.cpp b/src/download.cpp @@ -0,0 +1,171 @@ +#include "download.hpp" + +#include <cstdlib> + +#include <WDL/jnetlib/httpget.h> +#include <WDL/jnetlib/jnetlib.h> + +#include <reaper_plugin_functions.h> + +std::vector<Download *> Download::s_active; + +static const int DOWNLOAD_TIMEOUT = 60 * 10; + +Download::Download(const char *url) + : m_threadHandle(0), m_stop(false), m_status(0) +{ + m_url = url; +} + +Download::~Download() +{ + stop(); +} + +void Download::addCallback(const DownloadCallback &callback) +{ + m_callback.push_back(callback); +} + +void Download::timerTick() // static +{ + std::vector<Download *> &activeDownloads = Download::s_active; + const int size = activeDownloads.size(); + + for(int i = 0; i < size; i++) { + Download *download = activeDownloads[i]; + + if(!download->isFinished()) + continue; + + // this need to be done before execCallback in case one of the callback + // deletes the download object + activeDownloads.erase(activeDownloads.begin() + i); + + download->execCallbacks(); + } + + if(Download::s_active.empty()) + plugin_register("-timer", (void*)timerTick); +} + + +Download::StartCode Download::start() +{ + if(m_threadHandle) + return AlreadyRunning; + + s_active.push_back(this); + plugin_register("timer", (void*)timerTick); + + m_threadHandle = CreateThread(NULL, 0, worker, (void *)this, 0, 0); + return Started; +} + +void Download::stop() +{ + WDL_MutexLock lock(&m_mutex); + + if(!m_threadHandle) + return; + + m_stop = true; + + if(!isFinished()) + s_active.erase(std::remove(s_active.begin(), s_active.end(), this)); + + WaitForSingleObject(m_threadHandle, INFINITE); + CloseHandle(m_threadHandle); + m_threadHandle = 0; + + m_stop = false; +}; + +DWORD WINAPI Download::worker(void *ptr) // static +{ + JNL::open_socketlib(); + + Download *download = static_cast<Download *>(ptr); + + JNL_HTTPGet agent; + agent.addheader("Accept: */*"); + agent.addheader("User-Agent: ReaPack (REAPER)"); + agent.connect(download->url()); + + const time_t startTime = time(NULL); + + while(agent.run() == 0) { + if(download->isCancelled()) { + download->finish(-2, ""); + JNL::close_socketlib(); + return 1; + } + else if(time(NULL) - startTime >= DOWNLOAD_TIMEOUT) { + download->finish(-3, ""); + JNL::close_socketlib(); + return 1; + } + + Sleep(50); + } + + const int status = agent.getreplycode(); + const int size = agent.bytes_available(); + + char *buffer = new char[size]; + agent.get_bytes(buffer, size); + + download->finish(status, buffer); + + delete[] buffer; + + JNL::close_socketlib(); + + return 0; +} + +void Download::finish(const int status, const std::string &contents) +{ + WDL_MutexLock lock(&m_mutex); + + // called from the worker thread + m_finished = true; + m_status = status; + m_contents = contents; +} + +void Download::execCallbacks() +{ + // always called from the main thread + + for(DownloadCallback callback : m_callback) + callback(status(), contents()); +} + +int Download::status() +{ + WDL_MutexLock lock(&m_mutex); + + return m_status; +} + +const std::string &Download::contents() +{ + WDL_MutexLock lock(&m_mutex); + + return m_contents; +} + +bool Download::isFinished() +{ + WDL_MutexLock lock(&m_mutex); + + return m_finished; +} + +bool Download::isCancelled() +{ + WDL_MutexLock lock(&m_mutex); + + return m_stop; +} diff --git a/src/download.hpp b/src/download.hpp @@ -0,0 +1,58 @@ +#ifndef REAPACK_DOWNLOAD_HPP +#define REAPACK_DOWNLOAD_HPP + +#include <functional> +#include <string> +#include <vector> + +#include <WDL/mutex.h> + +#include <reaper_plugin.h> + +typedef std::function<void(int, const std::string &)> DownloadCallback; + +class Download { +public: + enum StartCode { + Started, + AlreadyRunning, + FileExists, + WriteError, + }; + + Download(const char *url); + ~Download(); + + const char *url() const { return m_url; } + + bool isFinished(); + bool isCancelled(); + int status(); + const std::string &contents(); + + void addCallback(const DownloadCallback &); + StartCode start(); + void stop(); + +private: + static std::vector<Download *> s_active; + + static void timerTick(); + static DWORD WINAPI worker(void *ptr); + + void finish(const int status, const std::string &contents); + void execCallbacks(); + + HANDLE m_threadHandle; + bool m_stop; + bool m_finished; + int m_status; + std::string m_contents; + + const char *m_url; + std::vector<DownloadCallback> m_callback; + + WDL_Mutex m_mutex; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp @@ -1,7 +1,8 @@ #include "reapack.hpp" +#include "download.hpp" #define REAPERAPI_IMPLEMENT -#include "reaper_plugin_functions.h" +#include <reaper_plugin_functions.h> static ReaPack reapack; @@ -19,6 +20,13 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT( if(REAPERAPI_LoadAPI(rec->GetFunc) > 0) return 0; + Download *down = new Download("http://cfillion.tk/"); + down->addCallback([=](const int status, const std::string &contents) { + ShowMessageBox(contents.c_str(), std::to_string(status).c_str(), 0); + delete down; + }); + down->start(); + reapack.init(instance, rec); reapack.setupAction("REAPACKMGR", "ReaPack: Package Manager",