193 lines
No EOL
7.6 KiB
C++
193 lines
No EOL
7.6 KiB
C++
#pragma once
|
|
#include <drogon/HttpClient.h>
|
|
#include <drogon/utils/Utilities.h>
|
|
#include <functional>
|
|
#include <string>
|
|
|
|
// 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
|
|
// In backend/src/services/OmeClient.h
|
|
|
|
void getActiveStreams(std::function<void(bool, const Json::Value&)> callback) {
|
|
// Try the streams endpoint first
|
|
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();
|
|
LOG_DEBUG << "OME streams response: " << json.toStyledString();
|
|
|
|
// OME might return the streams in different formats
|
|
// Sometimes it's {"response": ["stream1", "stream2"]}
|
|
// Sometimes it's {"response": {"streams": ["stream1", "stream2"]}}
|
|
if (json.isMember("response")) {
|
|
callback(true, json);
|
|
} else {
|
|
// Wrap the response if needed
|
|
Json::Value wrapped;
|
|
wrapped["response"] = json;
|
|
callback(true, wrapped);
|
|
}
|
|
} catch (const std::exception& e) {
|
|
LOG_ERROR << "Failed to parse OME response: " << e.what();
|
|
Json::Value empty;
|
|
empty["response"] = Json::arrayValue;
|
|
callback(false, empty);
|
|
}
|
|
} else {
|
|
LOG_ERROR << "Failed to get active streams from OME";
|
|
Json::Value empty;
|
|
empty["response"] = Json::arrayValue;
|
|
callback(false, empty);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Get stats for a specific stream
|
|
void getStreamStats(const std::string& streamKey,
|
|
std::function<void(bool, const Json::Value&)> 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<void(bool, const Json::Value&)> 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<void(bool)> 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;
|
|
}
|
|
}; |