52 lines
3.8 KiB
C++
52 lines
3.8 KiB
C++
/* Разбор снимка CHAT_EVT_STREAM_SOURCES для шапки чата и отдельных окон просмотра.
|
|
* Формат: group:8BE, valid:1 (0/1), JSON-массив UTF-8 без завершающего NUL.
|
|
* parseStreamSources заменяет out целиком только при успехе; ошибка сохраняет прежний out и логируется в stream.
|
|
* Успешный разбор с valid=false сообщает недоступность: UI сохраняет прежний каталог, а не очищает его.
|
|
* Разбор не сверяет выбранную группу, членство или подписи; получатель проверяет group перед применением. */
|
|
#pragma once
|
|
#include <QJsonDocument>
|
|
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
#include <QSet>
|
|
#include <QtEndian>
|
|
#include "../../../lib/debug_config.h"
|
|
|
|
struct LiveStreamSource {
|
|
quint64 id, epoch; // Ненулевые ID узла-источника и эпоха его публикации; ID уникален внутри снимка.
|
|
QString name; // Отображаемое имя; пустое имя JSON заменяется десятичным ID.
|
|
bool self = false; // Собственная публикация: числовое поле self из JSON, ненулевое после toInt.
|
|
};
|
|
struct StreamSourceSnapshot {
|
|
quint64 group = 0; // Ненулевой ID CHAT-группы при успешном разборе.
|
|
bool valid = false; // Каталог доступен; false не означает подтверждённое отсутствие трансляций.
|
|
QList<LiveStreamSource> sources; // Порядок JSON; применять к UI только при valid=true.
|
|
};
|
|
|
|
// data заимствован на время вызова; length — полное событие, включая 9-байтный префикс.
|
|
// true: JSON-массив с уникальными ненулевыми id (десятичная строка), epoch (hex-строка) и строковым name.
|
|
// self необязателен: отсутствующее/неприводимое к int значение даёт false. false: out не меняется.
|
|
inline bool parseStreamSources(const uint8_t* data, int length, StreamSourceSnapshot& out) {
|
|
if (!data || length < 11 || data[8] > 1) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop source event invalid bytes=%d", length); return false;
|
|
}
|
|
StreamSourceSnapshot next; next.group = qFromBigEndian<quint64>(data); next.valid = data[8];
|
|
QJsonParseError error;
|
|
auto document = QJsonDocument::fromJson(QByteArray(reinterpret_cast<const char*>(data + 9), length - 9), &error);
|
|
if (!next.group || error.error != QJsonParseError::NoError || !document.isArray()) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop source JSON invalid group=%llx error=%s",
|
|
(unsigned long long)next.group, qPrintable(error.errorString())); return false;
|
|
}
|
|
QSet<quint64> seen;
|
|
for (const auto& value : document.array()) {
|
|
auto member = value.toObject(); bool idOk = false, epochOk = false;
|
|
quint64 id = member.value("id").toString().toULongLong(&idOk);
|
|
quint64 epoch = member.value("epoch").toString().toULongLong(&epochOk, 16);
|
|
if (!idOk || !epochOk || !id || !epoch || seen.contains(id) || !member.value("name").isString()) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop source identity invalid group=%llx", (unsigned long long)next.group); return false;
|
|
}
|
|
QString name = member.value("name").toString(); if (name.isEmpty()) name = QString::number(id);
|
|
next.sources.append({id, epoch, name, member.value("self").toInt() != 0}); seen.insert(id);
|
|
}
|
|
out = std::move(next); return true;
|
|
}
|