#pragma once #include #include #include #include // TODO: Consider implementing OME webhooks for real-time updates instead of polling // OME supports webhooks for stream events (start/stop/etc) which would be more efficient // than polling. See: https://airensoft.gitbook.io/ovenmediaengine/access-control/admission-webhooks class OmeClient { public: static OmeClient& getInstance() { static OmeClient instance; return instance; } // Get list of active streams void getActiveStreams(std::function callback) { auto request = createRequest(drogon::Get, "/v1/vhosts/default/apps/app/streams"); getClient()->sendRequest(request, [callback](drogon::ReqResult result, const drogon::HttpResponsePtr& response) { if (result == drogon::ReqResult::Ok && response && response->getStatusCode() == drogon::k200OK) { try { Json::Value json = *response->getJsonObject(); callback(true, json); } catch (const std::exception& e) { LOG_ERROR << "Failed to parse OME response: " << e.what(); Json::Value empty; callback(false, empty); } } else { LOG_ERROR << "Failed to get active streams from OME"; Json::Value empty; callback(false, empty); } }); } // Get stats for a specific stream void getStreamStats(const std::string& streamKey, std::function callback) { std::string path = "/v1/stats/current/vhosts/default/apps/app/streams/" + streamKey; auto request = createRequest(drogon::Get, path); getClient()->sendRequest(request, [callback](drogon::ReqResult result, const drogon::HttpResponsePtr& response) { if (result == drogon::ReqResult::Ok && response) { if (response->getStatusCode() == drogon::k200OK) { try { Json::Value json = *response->getJsonObject(); callback(true, json); } catch (const std::exception& e) { LOG_ERROR << "Failed to parse stats response: " << e.what(); Json::Value empty; callback(false, empty); } } else { // Not found or error - return empty but success (stream offline) Json::Value empty; callback(true, empty); } } else { LOG_ERROR << "Request to OME failed"; Json::Value empty; callback(false, empty); } }); } // Get detailed stream info including track metadata (resolution, codec, etc.) void getStreamInfo(const std::string& streamKey, std::function callback) { std::string path = "/v1/vhosts/default/apps/app/streams/" + streamKey; auto request = createRequest(drogon::Get, path); getClient()->sendRequest(request, [callback](drogon::ReqResult result, const drogon::HttpResponsePtr& response) { if (result == drogon::ReqResult::Ok && response) { if (response->getStatusCode() == drogon::k200OK) { try { Json::Value json = *response->getJsonObject(); callback(true, json); } catch (const std::exception& e) { LOG_ERROR << "Failed to parse stream info response: " << e.what(); Json::Value empty; callback(false, empty); } } else { // Stream not found or error Json::Value empty; callback(false, empty); } } else { LOG_ERROR << "Stream info request to OME failed"; Json::Value empty; callback(false, empty); } }); } // Disconnect a stream void disconnectStream(const std::string& streamId, std::function callback) { std::string path = "/v1/vhosts/default/apps/app/streams/" + streamId; auto request = createRequest(drogon::Delete, path); getClient()->sendRequest(request, [callback](drogon::ReqResult result, const drogon::HttpResponsePtr& response) { bool success = (result == drogon::ReqResult::Ok && response && response->getStatusCode() == drogon::k200OK); callback(success); }); } private: OmeClient() = default; ~OmeClient() = default; OmeClient(const OmeClient&) = delete; OmeClient& operator=(const OmeClient&) = delete; std::string getBaseUrl() { // Check environment variable first const char* envUrl = std::getenv("OME_API_URL"); if (envUrl) { return std::string(envUrl); } // Try to get from Drogon config try { const auto& config = drogon::app().getCustomConfig(); if (config.isMember("ome") && config["ome"].isMember("api_url")) { return config["ome"]["api_url"].asString(); } } catch (...) { // Config not available } return "http://ovenmediaengine:8081"; // Default } std::string getApiToken() { // Check environment variable first const char* envToken = std::getenv("OME_API_TOKEN"); if (envToken) { return std::string(envToken); } // Try to get from Drogon config try { const auto& config = drogon::app().getCustomConfig(); if (config.isMember("ome") && config["ome"].isMember("api_token")) { return config["ome"]["api_token"].asString(); } } catch (...) { // Config not available } return "your-api-token"; // Default } drogon::HttpClientPtr getClient() { return drogon::HttpClient::newHttpClient(getBaseUrl()); } drogon::HttpRequestPtr createRequest(drogon::HttpMethod method, const std::string& path) { auto request = drogon::HttpRequest::newHttpRequest(); request->setMethod(method); request->setPath(path); // Add authorization header (OME uses Basic auth with token as username) const auto token = getApiToken(); const auto b64 = drogon::utils::base64Encode(token); request->addHeader("Authorization", std::string("Basic ") + b64); return request; } };