first commit

This commit is contained in:
2026-01-26 22:26:19 +03:00
commit 31fccd85f2
95 changed files with 115400 additions and 0 deletions
+418
View File
@@ -0,0 +1,418 @@
// emoteprovider.cpp
#include "emoteprovider.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
// EmoteProvider implementation
EmoteProvider::EmoteProvider(QObject *parent)
: QObject(parent)
, m_networkManager(new QNetworkAccessManager(this)) {
}
EmoteProvider::~EmoteProvider() {
// QNetworkAccessManager будет удален автоматически как дочерний объект
}
void EmoteProvider::setLogCallback(LogCallback callback) {
m_logCallback = callback;
}
void EmoteProvider::log(const QString &method, const QString &message, LogLevel level) {
if (m_logCallback) {
m_logCallback(metaObject()->className(), method, message, level);
}
}
// BTTVProvider implementation
BTTVProvider::BTTVProvider(QObject *parent)
: EmoteProvider(parent) {
}
BTTVProvider::~BTTVProvider() {
m_emotes.clear();
}
void BTTVProvider::fetchGlobal() {
log("fetchGlobal", "Fetching global BTTV emotes", LOG_INFO);
QNetworkRequest request(QUrl(getBaseUrl() + "emotes/global"));
request.setHeader(QNetworkRequest::UserAgentHeader,
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36");
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished,
this, [this, reply]() { onGlobalReplyFinished(reply); });
}
void BTTVProvider::fetchCustom(const QString &userId) {
if (userId.isEmpty()) {
log("fetchCustom", "User ID is empty", LOG_WARNING);
return;
}
log("fetchCustom", QString("Fetching custom BTTV emotes for user: %1").arg(userId), LOG_INFO);
m_lastUserId = userId;
QNetworkRequest request(QUrl(getBaseUrl() + "users/twitch/" + userId));
request.setHeader(QNetworkRequest::UserAgentHeader,
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36");
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished,
this, [this, reply]() { onCustomReplyFinished(reply); });
}
QString BTTVProvider::getEmoteUrl(const QString &emoteName) {
for (const auto &emote : m_emotes) {
if (emote.code == emoteName) {
return emote.url();
}
}
return QString();
}
void BTTVProvider::onGlobalReplyFinished(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
log("onGlobalReplyFinished",
QString("Network error: %1").arg(reply->errorString()),
LOG_ERROR);
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
parseGlobalResponse(data);
reply->deleteLater();
emit emotesLoaded();
}
void BTTVProvider::onCustomReplyFinished(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
log("onCustomReplyFinished",
QString("Network error: %1").arg(reply->errorString()),
LOG_ERROR);
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
parseCustomResponse(data, m_lastUserId);
reply->deleteLater();
emit emotesLoaded();
}
void BTTVProvider::parseGlobalResponse(const QByteArray &data) {
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
log("parseGlobalResponse",
QString("JSON parse error: %1").arg(parseError.errorString()),
LOG_ERROR);
return;
}
if (!doc.isArray()) {
log("parseGlobalResponse", "Expected JSON array", LOG_ERROR);
return;
}
QJsonArray array = doc.array();
m_emotes.clear();
for (const QJsonValue &value : array) {
if (!value.isObject()) continue;
QJsonObject obj = value.toObject();
BTTVEmote emote;
emote.id = obj.value("id").toString();
emote.code = obj.value("code").toString();
if (!emote.id.isEmpty() && !emote.code.isEmpty()) {
m_emotes.append(emote);
}
}
log("parseGlobalResponse",
QString("Loaded %1 global emotes").arg(m_emotes.size()),
LOG_INFO);
}
void BTTVProvider::parseCustomResponse(const QByteArray &data, const QString &userId) {
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
log("parseCustomResponse",
QString("JSON parse error: %1").arg(parseError.errorString()),
LOG_ERROR);
return;
}
if (!doc.isObject()) {
log("parseCustomResponse", "Expected JSON object", LOG_ERROR);
return;
}
QJsonObject root = doc.object();
auto parseEmotesArray = [this](const QJsonArray &array) {
for (const QJsonValue &value : array) {
if (!value.isObject()) continue;
QJsonObject obj = value.toObject();
BTTVEmote emote;
emote.id = obj.value("id").toString();
emote.code = obj.value("code").toString();
if (!emote.id.isEmpty() && !emote.code.isEmpty()) {
m_emotes.append(emote);
}
}
};
// Очищаем только пользовательские эмодзи (можно оптимизировать)
// В реальном приложении нужно хранить отдельно глобальные и пользовательские
// Парсим channelEmotes
if (root.contains("channelEmotes") && root["channelEmotes"].isArray()) {
parseEmotesArray(root["channelEmotes"].toArray());
}
// Парсим sharedEmotes
if (root.contains("sharedEmotes") && root["sharedEmotes"].isArray()) {
parseEmotesArray(root["sharedEmotes"].toArray());
}
log("parseCustomResponse",
QString("Loaded %1 custom emotes for user %2").arg(m_emotes.size()).arg(userId),
LOG_INFO);
}
// SevenTVProvider implementation
SevenTVProvider::SevenTVProvider(QObject *parent)
: EmoteProvider(parent) {
}
SevenTVProvider::~SevenTVProvider() {
m_emotes.clear();
}
void SevenTVProvider::fetchGlobal() {
log("fetchGlobal", "Fetching global 7TV emotes", LOG_INFO);
QNetworkRequest request(QUrl(getBaseUrl() + "emote-sets/global"));
request.setHeader(QNetworkRequest::UserAgentHeader,
"Mozilla/5.0");
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished,
this, [this, reply]() { onGlobalReplyFinished(reply); });
}
void SevenTVProvider::fetchCustom(const QString &userId) {
if (userId.isEmpty()) {
log("fetchCustom", "User ID is empty", LOG_WARNING);
return;
}
log("fetchCustom", QString("Fetching custom 7TV emotes for user: %1").arg(userId), LOG_INFO);
m_lastUserId = userId;
QNetworkRequest request(QUrl(getBaseUrl() + "users/twitch/" + userId));
request.setHeader(QNetworkRequest::UserAgentHeader,
"Mozilla/5.0");
QNetworkReply *reply = m_networkManager->get(request);
connect(reply, &QNetworkReply::finished,
this, [this, reply]() { onCustomReplyFinished(reply); });
}
QString SevenTVProvider::getEmoteUrl(const QString &emoteName) {
for (const auto &emote : m_emotes) {
if (emote.code == emoteName) {
return emote.url;
}
}
return QString();
}
void SevenTVProvider::onGlobalReplyFinished(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
log("onGlobalReplyFinished",
QString("Network error: %1").arg(reply->errorString()),
LOG_ERROR);
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
parseGlobalResponse(data);
reply->deleteLater();
emit emotesLoaded();
}
void SevenTVProvider::onCustomReplyFinished(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
log("onCustomReplyFinished",
QString("Network error: %1").arg(reply->errorString()),
LOG_ERROR);
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
parseCustomResponse(data, m_lastUserId);
reply->deleteLater();
emit emotesLoaded();
}
void SevenTVProvider::parseGlobalResponse(const QByteArray &data) {
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
log("parseGlobalResponse",
QString("JSON parse error: %1").arg(parseError.errorString()),
LOG_ERROR);
return;
}
if (!doc.isObject()) {
log("parseGlobalResponse", "Expected JSON object", LOG_ERROR);
return;
}
QJsonObject root = doc.object();
m_emotes.clear();
if (!root.contains("emotes") || !root["emotes"].isArray()) {
log("parseGlobalResponse", "No emotes array found", LOG_ERROR);
return;
}
QJsonArray emotesArray = root["emotes"].toArray();
for (const QJsonValue &emoteValue : emotesArray) {
if (!emoteValue.isObject()) continue;
QJsonObject emoteObj = emoteValue.toObject();
SevenTVEmote emote;
emote.id = emoteObj.value("id").toString();
emote.code = emoteObj.value("name").toString();
// Парсим URL
if (emoteObj.contains("data") && emoteObj["data"].isObject()) {
QJsonObject dataObj = emoteObj["data"].toObject();
if (dataObj.contains("host") && dataObj["host"].isObject()) {
QJsonObject hostObj = dataObj["host"].toObject();
if (hostObj.contains("url")) {
QString baseUrl = "https:" + hostObj["url"].toString();
if (hostObj.contains("files") && hostObj["files"].isArray()) {
QJsonArray filesArray = hostObj["files"].toArray();
if (!filesArray.isEmpty() && filesArray[0].isObject()) {
QJsonObject fileObj = filesArray[0].toObject();
if (fileObj.contains("name")) {
emote.url = baseUrl + "/" + fileObj["name"].toString();
}
}
}
}
}
}
if (!emote.id.isEmpty() && !emote.code.isEmpty() && !emote.url.isEmpty()) {
m_emotes.append(emote);
}
}
log("parseGlobalResponse",
QString("Loaded %1 global emotes").arg(m_emotes.size()),
LOG_INFO);
}
void SevenTVProvider::parseCustomResponse(const QByteArray &data, const QString &userId) {
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
log("parseCustomResponse",
QString("JSON parse error: %1").arg(parseError.errorString()),
LOG_ERROR);
return;
}
if (!doc.isObject()) {
log("parseCustomResponse", "Expected JSON object", LOG_ERROR);
return;
}
QJsonObject root = doc.object();
if (!root.contains("emote_set") || !root["emote_set"].isObject()) {
log("parseCustomResponse", "No emote_set found", LOG_ERROR);
return;
}
QJsonObject emoteSet = root["emote_set"].toObject();
if (!emoteSet.contains("emotes") || !emoteSet["emotes"].isArray()) {
log("parseCustomResponse", "No emotes array in emote_set", LOG_ERROR);
return;
}
QJsonArray emotesArray = emoteSet["emotes"].toArray();
for (const QJsonValue &emoteValue : emotesArray) {
if (!emoteValue.isObject()) continue;
QJsonObject emoteObj = emoteValue.toObject();
SevenTVEmote emote;
emote.id = emoteObj.value("id").toString();
emote.code = emoteObj.value("name").toString();
// Парсим URL (аналогично parseGlobalResponse)
if (emoteObj.contains("data") && emoteObj["data"].isObject()) {
QJsonObject dataObj = emoteObj["data"].toObject();
if (dataObj.contains("host") && dataObj["host"].isObject()) {
QJsonObject hostObj = dataObj["host"].toObject();
if (hostObj.contains("url")) {
QString baseUrl = "https:" + hostObj["url"].toString();
if (hostObj.contains("files") && hostObj["files"].isArray()) {
QJsonArray filesArray = hostObj["files"].toArray();
if (!filesArray.isEmpty() && filesArray[0].isObject()) {
QJsonObject fileObj = filesArray[0].toObject();
if (fileObj.contains("name")) {
emote.url = baseUrl + "/" + fileObj["name"].toString();
}
}
}
}
}
}
if (!emote.id.isEmpty() && !emote.code.isEmpty() && !emote.url.isEmpty()) {
m_emotes.append(emote);
}
}
log("parseCustomResponse",
QString("Loaded %1 custom emotes for user %2").arg(m_emotes.size()).arg(userId),
LOG_INFO);
}