room_status.h
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
#pragma once
#include <atomic>
#include "volume_control.h"
#include "queue_uri_log.h"
#include "podcast_settings.h"
#include <mutex>
#include <sstream>
#include <string>
#include <vector>
namespace bridge {
struct ModeStatus {
std::string requested = "stream", active = "stream", state = "active", reason = "default-stream";
std::string path;
bool owned = false;
uint64_t occurrence = 0;
uint32_t renderedRate = 0;
std::vector<std::string> ownedUris;
std::string currentUri, lastFault, title;
};
// Passive snapshot registry, never a mode controller. A single room worker
// publishes queue observations and the complete diagnostic file synchronously.
class RoomStatus {
mutable std::mutex mutex;
ModeStatus value;
public:
ModeStatus status() const {
std::lock_guard<std::mutex> lock(mutex);
return value;
}
void publish(ModeStatus v) {
std::lock_guard<std::mutex> lock(mutex);
value = std::move(v);
}
static std::string quote(const std::string &t) {
std::string out = "\"";
for (unsigned char c : t) {
if (c == '\n') out += "\\n";
else if (c == '\r') out += "\\r";
else if (c == '\t') out += "\\t";
else {
if (c == '"' || c == '\\') out += '\\';
out += c >= 32 ? char(c) : '?';
}
}
return out + '"';
}
static std::string json(const ModeStatus &v) {
auto q = [](const std::string &t) {
std::string out = "\"";
for (unsigned char c : t) {
if (c == '"' || c == '\\') out += '\\';
out += c >= 32 ? char(c) : '?';
}
return out + '"';
};
std::ostringstream out;
out << "{\"requested\":" << q(v.requested) << ",\"active\":" << q(v.active)
<< ",\"state\":" << q(v.state) << ",\"reason\":" << q(v.reason);
const auto &volume = roomVolumeSettings();
out << ",\"lms_volume\":" << q(volume.speaker ? "speaker" : "off")
<< ",\"lms_volume_source\":" << q(volume.source) << ",\"volume_max\":" << volume.maximum;
for (const auto &setting : roomPodcastSettings())
out << "," << q(setting.key) << ":" << q(setting.enabled ? "yes" : "no")
<< "," << q(std::string(setting.key)+"_source") << ":" << q(setting.source);
if (!v.path.empty())
out << ",\"path\":" << q(v.path);
if (v.active == "queue") {
out << ",\"occurrence\":" << v.occurrence << ",\"owned\":" << (v.owned ? "true" : "false")
<< ",\"rendered_rate\":" << v.renderedRate << ",\"current_owned_item\":" << q(queueUriLog(v.currentUri))
<< ",\"last_fault\":" << q(queueUriLog(v.lastFault)) << ",\"owned_item_uris\":[";
for (size_t i = 0; i < v.ownedUris.size(); ++i) {
if (i)
out << ',';
out << q(queueUriLog(v.ownedUris[i]));
}
out << "]";
if (v.path == "relay") out << ",\"title\":" << quote(v.title);
out << '}';
} else {
// Preserve the stream diagnostic's established representation.
out << ",\"occurrence\":0,\"epoch\":0,\"generation\":1,\"owned\":false,\"mirrored_ahead\":0,"
"\"last_fallback\":\"\",\"handoff_gap_ms\":-1,\"latched\":false";
if (v.requested == "queue")
out << ",\"lms_cli_slow_replies\":0,\"lms_cli_failed_replies\":0";
out << '}';
}
return out.str();
}
};
inline std::atomic<RoomStatus *> &statusRegistry() {
static auto *registry = new std::atomic<RoomStatus *>(nullptr);
return *registry;
}
inline RoomStatus *roomMode() { return statusRegistry().load(); }
inline void setRoomStatus(RoomStatus *value) { statusRegistry() = value; }
} // namespace bridge