first commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user