// emoteprovider.cpp #include "emoteprovider.h" #include #include #include #include // 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, LogLevelemt 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); }