1 /*
2  * Copyright © 2015-2016 Antti Lamminsalo
3  *
4  * This file is part of Orion.
5  *
6  * Orion is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with Orion.  If not, see <http://www.gnu.org/licenses/>.
13  */
14 
15 #include "networkmanager.h"
16 #include "../util/fileutils.h"
17 #include "../util/jsonparser.h"
18 #include "../util/m3u8parser.h"
19 #include <QNetworkConfiguration>
20 #include <QEventLoop>
21 #include <QSet>
22 #include <QUrlQuery>
23 #include "../model/settingsmanager.h"
24 
25 NetworkManager *NetworkManager::singleton = 0;
26 
NetworkManager(QNetworkAccessManager * man)27 NetworkManager::NetworkManager(QNetworkAccessManager *man) : QObject(man)
28 {
29     operation = man;
30 
31     initReplayChat();
32 
33     //Select interface
34     connectionOK = false;
35     testNetworkInterface();
36 
37     //SSL errors handle (down the drain)
38     connect(operation, &QNetworkAccessManager::sslErrors, this, &NetworkManager::handleSslErrors);
39 
40     //Handshake
41     operation->connectToHost(TWITCH_API);
42 
43     //Set up offline poller
44     offlinePoller.setInterval(2000);
45     connect(&offlinePoller, &QTimer::timeout, this, &NetworkManager::testNetworkInterface);
46 
47     //Set up listening to access token changes
48     connect(SettingsManager::getInstance(), &SettingsManager::accessTokenChanged, this, &NetworkManager::setAccessToken);
49     setAccessToken(SettingsManager::getInstance()->accessToken());
50 
51     lastVodChatRequest = nullptr;
52 }
53 
setAccessToken(const QString & accessToken)54 void NetworkManager::setAccessToken(const QString &accessToken)
55 {
56     access_token = accessToken;
57 }
58 
~NetworkManager()59 NetworkManager::~NetworkManager()
60 {
61     offlinePoller.stop();
62     qDebug() << "Destroyer: NetworkManager";
63     //operation->deleteLater();
64     teardownReplayChat();
65 }
66 
initialize(QNetworkAccessManager * mgr)67 void NetworkManager::initialize(QNetworkAccessManager *mgr)
68 {
69     singleton = new NetworkManager(mgr);
70 }
71 
testNetworkInterface()72 void NetworkManager::testNetworkInterface()
73 {
74     //Chooses a working network interface from interfaces list, if default configuration doesn't work
75 
76     QNetworkConfigurationManager conf;
77     QString identifier;
78 
79     QEventLoop loop;
80     connect(this, &NetworkManager::finishedConnectionTest, &loop, &QEventLoop::quit);
81 
82     //Test default configuration
83     operation->setConfiguration(conf.defaultConfiguration());
84 
85     testConnection();
86     loop.exec();
87 
88     if (connectionOK == true) {
89         qDebug() << "Selected default network configuration";
90         return;
91     }
92 
93     else {
94         qDebug() << "Failure on default configuration, attempt to choose another interaface..";
95 
96         foreach (const QNetworkInterface & interface, QNetworkInterface::allInterfaces())
97         {
98             if (!interface.isValid())
99                 continue;
100 
101 //            qDebug() << "Identifier: " << interface.name();
102 //            qDebug() << "HW addr: " << interface.hardwareAddress();
103 
104             bool isUp = interface.flags().testFlag(QNetworkInterface::IsUp);
105             bool isLoopback = interface.flags().testFlag(QNetworkInterface::IsLoopBack);
106             bool isActive = interface.flags().testFlag(QNetworkInterface::IsRunning);
107 //            bool isPtP = interface.flags().testFlag(QNetworkInterface::IsPointToPoint);
108 
109 //            qDebug() << "Properties: ";
110 //            qDebug() << (isUp ? "Is up" : "Is down");
111 
112 //            if (isLoopback)
113 //                qDebug() << "Loopback";
114 
115 //            qDebug() << (isActive ? "Active" : "Inactive");
116 
117 //            if (isPtP)
118 //                qDebug() << "Is Point-to-Point";
119 
120 //            qDebug() << "";
121 
122             if (isUp && isActive && !isLoopback) {
123                 identifier = interface.name();
124                 qDebug() << "Testing connection for interface " << identifier;
125                 operation->setConfiguration(conf.configurationFromIdentifier(identifier));
126 
127                 testConnection();
128                 loop.exec();
129 
130                 if (connectionOK == true) {
131                     qDebug() << "Success!";
132                     return;
133                 }
134 
135                 else
136                     qDebug() << "Failure, trying another interface...";
137             }
138 
139         }
140     }
141 
142     if (!connectionOK) {
143         operation->setConfiguration(conf.defaultConfiguration());
144         offlinePoller.start();
145     }
146 }
147 
networkAccess()148 bool NetworkManager::networkAccess() {
149     return connectionOK;
150 }
151 
testConnection()152 void NetworkManager::testConnection()
153 {
154     QNetworkRequest request;
155 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
156     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
157     request.setRawHeader("Client-ID", getClientId().toUtf8());
158     request.setUrl(QUrl(KRAKEN_API));
159 
160     QNetworkReply *reply = operation->get(request);
161 
162     connect(reply, &QNetworkReply::finished, this, &NetworkManager::testConnectionReply);
163 }
164 
testConnectionReply()165 void NetworkManager::testConnectionReply()
166 {
167     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
168 
169 //    if (reply->error() == QNetworkReply::NoError)
170 //        qDebug() << "Got response: " << reply->readAll();
171 
172     handleNetworkError(reply);
173 
174     emit finishedConnectionTest();
175 }
176 
checkVersion()177 void NetworkManager::checkVersion()
178 {
179 }
180 
181 /**
182  * @brief NetworkManager::getStream
183  * Gets single stream status. Usable for polling a channel's stream
184  */
getStream(const quint64 channelId)185 void NetworkManager::getStream(const quint64 channelId)
186 {
187     QString url = KRAKEN_API + QString("/streams/%1").arg(channelId);
188     QNetworkRequest request;
189 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
190     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
191     request.setRawHeader("Client-ID", getClientId().toUtf8());
192     request.setUrl(QUrl(url));
193 
194     QNetworkReply *reply = operation->get(request);
195 
196     connect(reply, &QNetworkReply::finished, this, &NetworkManager::streamReply);
197 }
198 
getStreams(const QString & url)199 void NetworkManager::getStreams(const QString &url)
200 {
201     //qDebug() << "GET: " << url;
202     QNetworkRequest request;
203 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
204     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
205     request.setRawHeader("Client-ID", getClientId().toUtf8());
206     request.setUrl(QUrl(url));
207 
208     QNetworkReply *reply = operation->get(request);
209 
210     connect(reply, &QNetworkReply::finished, this, &NetworkManager::allStreamsReply);
211 }
212 
getGames(const quint32 & offset,const quint32 & limit)213 void NetworkManager::getGames(const quint32 &offset, const quint32 &limit)
214 {
215     QNetworkRequest request;
216 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
217     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
218     request.setRawHeader("Client-ID", getClientId().toUtf8());
219     QString url = KRAKEN_API;
220     url += QString("/games/top?limit=%1").arg(limit)
221             + QString("&offset=%1").arg(offset);
222     request.setUrl(QUrl(url));
223 
224     QNetworkReply *reply = operation->get(request);
225 
226     connect(reply, &QNetworkReply::finished, this, &NetworkManager::gamesReply);
227 }
228 
searchChannels(const QString & query,const quint32 & offset,const quint32 & limit)229 void NetworkManager::searchChannels(const QString &query, const quint32 &offset, const quint32 &limit)
230 {
231     QNetworkRequest request;
232 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
233     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
234     request.setRawHeader("Client-ID", getClientId().toUtf8());
235     QString url = QString(KRAKEN_API)
236             + QString("/search/channels?query=") + QUrl::toPercentEncoding(query)
237             + QString("&offset=%1").arg(offset)
238             + QString("&limit=%1").arg(limit);
239 
240     qDebug() << "requesting" << url;
241     request.setUrl(QUrl(url));
242 
243     QNetworkReply *reply = operation->get(request);
244 
245     connect(reply, &QNetworkReply::finished, this, &NetworkManager::searchChannelsReply);
246 }
247 
searchGames(const QString & query)248 void NetworkManager::searchGames(const QString &query)
249 {
250     QNetworkRequest request;
251 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
252     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
253     request.setRawHeader("Client-ID", getClientId().toUtf8());
254     QString url = QString(KRAKEN_API)
255             + QString("/search/games?query=") + QUrl::toPercentEncoding(query);
256 
257     request.setUrl(QUrl(url));
258 
259     QNetworkReply *reply = operation->get(request);
260 
261     connect(reply, &QNetworkReply::finished, this, &NetworkManager::searchGamesReply);
262 }
263 
getFeaturedStreams()264 void NetworkManager::getFeaturedStreams()
265 {
266     QNetworkRequest request;
267 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
268     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
269     request.setRawHeader("Client-ID", getClientId().toUtf8());
270     QString url = QString(KRAKEN_API)
271             + "/streams/featured?limit=25&offset=0";
272     request.setUrl(QUrl(url));
273 
274     //qDebug() << url;
275 
276     QNetworkReply *reply = operation->get(request);
277 
278     connect(reply, &QNetworkReply::finished, this, &NetworkManager::featuredStreamsReply);
279 }
280 
getStreamsForGame(const QString & game,const quint32 & offset,const quint32 & limit)281 void NetworkManager::getStreamsForGame(const QString &game, const quint32 &offset, const quint32 &limit)
282 {
283     QNetworkRequest request;
284 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
285     request.setRawHeader("Client-ID", getClientId().toUtf8());
286     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
287     QString url = QString(KRAKEN_API)
288             + QString("/streams?game=") + QUrl::toPercentEncoding(game)
289             + QString("&offset=%1").arg(offset)
290             + QString("&limit=%1").arg(limit);
291     request.setUrl(QUrl(url));
292 
293     QNetworkReply *reply = operation->get(request);
294 
295     connect(reply, &QNetworkReply::finished, this, &NetworkManager::gameStreamsReply);
296 }
297 
getChannelPlaybackStream(const QString & channelName)298 void NetworkManager::getChannelPlaybackStream(const QString &channelName)
299 {
300     QString url = QString(TWITCH_API)
301             + QString("/channels/%1").arg(channelName)
302             + QString("/access_token");
303     QNetworkRequest request;
304 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
305     request.setRawHeader("Client-ID", getPrivateClientId().toUtf8());
306     request.setUrl(QUrl(url));
307 
308     request.setAttribute(QNetworkRequest::User, LIVE);
309 
310     QNetworkReply *reply = operation->get(request);
311 
312     connect(reply, &QNetworkReply::finished, this, &NetworkManager::streamExtractReply);
313 }
314 
getBroadcasts(const quint64 channelId,quint32 offset,quint32 limit)315 void NetworkManager::getBroadcasts(const quint64 channelId, quint32 offset, quint32 limit)
316 {
317     QString url = QString(KRAKEN_API)
318             + QString("/channels/%1/videos").arg(channelId)
319             + QString("?offset=%1").arg(offset)
320             + QString("&limit=%1").arg(limit);
321 
322     if (ONLY_BROADCASTS)
323         url += "&broadcast_type=archive";
324 
325     //if (USE_HLS)
326         //url += "&hls=true";
327 
328     QNetworkRequest request;
329 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
330     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
331     request.setRawHeader("Client-ID", getClientId().toUtf8());
332     request.setUrl(QUrl(url));
333 
334     QNetworkReply *reply = operation->get(request);
335 
336     connect(reply, &QNetworkReply::finished, this, &NetworkManager::broadcastsReply);
337 }
338 
getBroadcastPlaybackStream(const QString & vod)339 void NetworkManager::getBroadcastPlaybackStream(const QString &vod)
340 {
341     QString url = QString(TWITCH_API)
342             + QString("/vods/%1").arg(vod)
343             + QString("/access_token");
344     QNetworkRequest request;
345 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
346     request.setRawHeader("Client-ID", getPrivateClientId().toUtf8());
347     request.setUrl(QUrl(url));
348 
349     request.setAttribute(QNetworkRequest::User, VOD);
350 
351     QNetworkReply *reply = operation->get(request);
352 
353     connect(reply, &QNetworkReply::finished, this, &NetworkManager::streamExtractReply);
354 }
355 
getUser()356 void NetworkManager::getUser()
357 {
358     QString url = QString(KRAKEN_API) + "/user";
359     QString auth = "OAuth " + access_token;
360 
361     QNetworkRequest request;
362 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
363     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
364     request.setRawHeader("Client-ID", getClientId().toUtf8());
365     request.setUrl(QUrl(url));
366     request.setRawHeader(QString("Authorization").toUtf8(), auth.toUtf8());
367 
368     QNetworkReply *reply = operation->get(request);
369 
370     connect(reply, &QNetworkReply::finished, this, &NetworkManager::userReply);
371 }
372 
getUserFavourites(const quint64 userId,quint32 offset,quint32 limit)373 void NetworkManager::getUserFavourites(const quint64 userId, quint32 offset, quint32 limit)
374 {
375     if (!userId)
376         return;
377 
378     QString url = QString(KRAKEN_API) + "/users/" + QString::number(userId) + "/follows/channels"
379             + QString("?offset=%1").arg(offset)
380             + QString("&limit=%1").arg(limit);
381     QNetworkRequest request;
382 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
383     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
384     request.setRawHeader("Client-ID", getClientId().toUtf8());
385     request.setUrl(QUrl(url));
386     request.setAttribute(QNetworkRequest::User, (int) (offset + limit));
387 
388     QNetworkReply *reply = operation->get(request);
389 
390     connect(reply, &QNetworkReply::finished, this, &NetworkManager::favouritesReply);
391 }
392 
getEmoteSets(const QList<int> & emoteSetIDs)393 void NetworkManager::getEmoteSets(const QList<int> &emoteSetIDs) {
394     QList<QString> emoteSetsIDsStr;
395     for (auto id : emoteSetIDs) {
396         emoteSetsIDsStr.append(QString::number(id));
397     }
398 
399     QString url = QString(KRAKEN_API) + "/chat/emoticon_images"
400         + QString("?emotesets=") + emoteSetsIDsStr.join(',');
401     QString auth = "OAuth " + access_token;
402 
403     qDebug() << "Requesting" << url;
404 
405     QNetworkRequest request;
406 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
407     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
408     request.setRawHeader("Client-ID", getClientId().toUtf8());
409     request.setUrl(QUrl(url));
410     request.setRawHeader(QString("Authorization").toUtf8(), auth.toUtf8());
411 
412     QNetworkReply *reply = operation->get(request);
413 
414     connect(reply, &QNetworkReply::finished, this, &NetworkManager::emoteSetsReply);
415 }
416 
loadChatterList(const QString channel)417 void NetworkManager::loadChatterList(const QString channel) {
418     qDebug() << "Loading viewer list for" << channel;
419     const QString url = QString(TWITCH_TMI_USER_API) + channel + QString("/chatters");
420 
421     qDebug() << "Request" << url;
422 
423     QNetworkRequest request;
424 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
425     request.setUrl(url);
426 
427     QNetworkReply *reply = operation->get(request);
428 
429     connect(reply, &QNetworkReply::finished, this, &NetworkManager::chatterListReply);
430 }
431 
getBlockedUserList(const quint64 userId,const quint32 offset,const quint32 limit)432 void NetworkManager::getBlockedUserList(const quint64 userId, const quint32 offset, const quint32 limit) {
433     qDebug() << "Loading blocked user list for user" << userId;
434     const QString url = QString(KRAKEN_API) + QString("/users/") + QString::number(userId) + QString("/blocks?offset=" + QString::number(offset) + "&limit=" + QString::number(limit) );
435     qDebug() << "Request" << url;
436 
437     QNetworkRequest request;
438 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
439     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
440     request.setRawHeader("Client-ID", getClientId().toUtf8());
441     request.setUrl(url);
442 
443     int nextOffset = offset + limit;
444     request.setAttribute(QNetworkRequest::User, nextOffset);
445 
446     QString auth = "OAuth " + access_token;
447     request.setRawHeader(QString("Authorization").toUtf8(), auth.toUtf8());
448 
449     QNetworkReply *reply = operation->get(request);
450 
451     connect(reply, &QNetworkReply::finished, this, &NetworkManager::blockedUserListReply);
452 }
453 
editUserBlock(const quint64 myUserId,const QString & blockUsername,const bool isBlock)454 void NetworkManager::editUserBlock(const quint64 myUserId, const QString & blockUsername, const bool isBlock) {
455     const QString url = QString(KRAKEN_API) + QString("/users?login=") + QUrl::toPercentEncoding(blockUsername);
456 
457     QNetworkRequest request;
458 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
459     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
460     request.setRawHeader("Client-ID", getClientId().toUtf8());
461     request.setUrl(url);
462 
463     request.setAttribute(QNetworkRequest::User, myUserId);
464     request.setAttribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 1), blockUsername);
465     request.setAttribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 2), isBlock);
466     request.setAttribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 3), access_token);
467 
468     QNetworkReply *reply = operation->get(request);
469 
470     connect(reply, &QNetworkReply::finished, this, &NetworkManager::blockUserLookupReply);
471 }
472 
blockUserLookupReply()473 void NetworkManager::blockUserLookupReply() {
474     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
475 
476     if (!handleNetworkError(reply)) {
477         return;
478     }
479 
480     quint64 myUserId = reply->request().attribute(QNetworkRequest::User).toULongLong();
481     QString blockUsername = reply->request().attribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 1)).toString();
482     bool isBlock = reply->request().attribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 2)).toBool();
483     //QString access_token = reply->request().attribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 3)).toString();
484 
485     QByteArray data = reply->readAll();
486     const auto & userIds = JsonParser::parseUsers(data);
487 
488     if (userIds.length() == 0 || userIds[0] == 0) {
489         qDebug() << "userId lookup failed for" << blockUsername;
490     }
491 
492     quint64 blockUserId = userIds[0];
493 
494     editUserBlockWithId(myUserId, blockUsername, blockUserId, isBlock);
495 }
496 
editUserBlockWithId(const quint64 myUserId,const QString & blockUsername,const quint64 blockUserId,const bool isBlock)497 void NetworkManager::editUserBlockWithId(const quint64 myUserId, const QString & blockUsername, const quint64 blockUserId, const bool isBlock) {
498     qDebug() << "Setting block for user" << blockUserId << "to" << isBlock << "for user" << myUserId;
499     const QString url = QString(KRAKEN_API) + QString("/users/") + QString::number(myUserId) + QString("/blocks/") + QString::number(blockUserId);
500     qDebug() << "Request" << url;
501 
502     QNetworkRequest request;
503 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
504     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
505     request.setRawHeader("Client-ID", getClientId().toUtf8());
506     request.setUrl(url);
507 
508     request.setAttribute(QNetworkRequest::User, myUserId);
509     request.setAttribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 1), blockUsername);
510     request.setAttribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 2), isBlock);
511 
512     QString auth = "OAuth " + access_token;
513     request.setRawHeader(QString("Authorization").toUtf8(), auth.toUtf8());
514 
515     QNetworkReply *reply;
516     if (isBlock) {
517         reply = operation->put(request, "");
518     }
519     else {
520         reply = operation->deleteResource(request);
521     }
522 
523     connect(reply, &QNetworkReply::finished, this, &NetworkManager::blockUserReply);
524 }
525 
blockUserReply()526 void NetworkManager::blockUserReply() {
527     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
528 
529     if (!handleNetworkError(reply)) {
530         int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
531         if (statusCode == 401) {
532             qWarning() << "Warning: Not authorized to edit blocked users list; logout and log in again to update OAuth scopes";
533         }
534         return;
535     }
536 
537     quint64 myUserId = reply->request().attribute(QNetworkRequest::User).toULongLong();
538     QString blockUsername = reply->request().attribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 1)).toString();
539     bool isBlock = reply->request().attribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 2)).toBool();
540 
541     if (isBlock) {
542         emit userBlocked(myUserId, blockUsername);
543     }
544     else {
545         emit userUnblocked(myUserId, blockUsername);
546     }
547 }
548 
chatterListReply()549 void NetworkManager::chatterListReply() {
550     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
551 
552     if (!handleNetworkError(reply)) {
553         return;
554     }
555 
556     QByteArray data = reply->readAll();
557 
558     //qDebug() << data;
559 
560     QMap<QString, QList<QString>> ret = JsonParser::parseChatterList(data);
561 
562     emit chatterListLoadOperationFinished(ret);
563 
564     reply->deleteLater();
565 }
566 
blockedUserListReply()567 void NetworkManager::blockedUserListReply() {
568     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
569 
570     if (!handleNetworkError(reply)) {
571         int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
572         if (statusCode == 401) {
573             qWarning() << "Warning: Not authorized to read blocked users list; logout and log in again to update OAuth scopes";
574         }
575         return;
576     }
577 
578     QByteArray data = reply->readAll();
579 
580     auto result = JsonParser::parseBlockList(data);
581 
582     int nextOffset = reply->request().attribute(QNetworkRequest::User).toInt();
583 
584     emit blockedUserListLoadOperationFinished(result.items, nextOffset, result.total);
585 
586     reply->deleteLater();
587 }
588 
getVodChatPiece(quint64 vodId,quint64 offset)589 void NetworkManager::getVodChatPiece(quint64 vodId, quint64 offset) {
590     QString url = QString(TWITCH_API_V5) + QString("/videos/%2/comments?content_offset_seconds=%1").arg(offset).arg(vodId);
591 
592     qDebug() << "Requesting" << url;
593 
594     QNetworkRequest request;
595 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
596     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
597     request.setRawHeader("Client-ID", getClientId().toUtf8());
598     request.setUrl(url);
599 
600     QNetworkReply *reply = operation->get(request);
601 
602     lastVodChatRequest = reply;
603 
604     connect(reply, &QNetworkReply::finished, this, &NetworkManager::vodChatPieceReply);
605 }
606 
getNextVodChatPiece(quint64 vodId,QString cursor)607 void NetworkManager::getNextVodChatPiece(quint64 vodId, QString cursor) {
608     QString url = QString(TWITCH_API_V5) + QString("/videos/%2/comments?cursor=%1").arg(cursor).arg(vodId);
609 
610     qDebug() << "Requesting" << url;
611 
612     QNetworkRequest request;
613 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
614     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
615     request.setRawHeader("Client-ID", getClientId().toUtf8());
616     request.setUrl(url);
617 
618     QNetworkReply *reply = operation->get(request);
619 
620     lastVodChatRequest = reply;
621 
622     connect(reply, &QNetworkReply::finished, this, &NetworkManager::vodChatPieceReply);
623 }
624 
cancelLastVodChatRequest()625 void NetworkManager::cancelLastVodChatRequest() {
626     if (lastVodChatRequest != nullptr) {
627         lastVodChatRequest->abort();
628         lastVodChatRequest = nullptr;
629     }
630 }
631 
resetVodChat()632 void NetworkManager::resetVodChat() {
633     replayChatPartNum = 0;
634     curChatReplayDedupeBatch->clear();
635     prevChatReplayDedupeBatch->clear();
636 }
637 
initReplayChat()638 void NetworkManager::initReplayChat() {
639     curChatReplayDedupeBatch = new QSet<QString>();
640     prevChatReplayDedupeBatch = new QSet<QString>();
641 }
642 
teardownReplayChat()643 void NetworkManager::teardownReplayChat() {
644     delete curChatReplayDedupeBatch;
645     delete prevChatReplayDedupeBatch;
646 }
647 
filterReplayChat(QList<ReplayChatMessage> & replayChat)648 void NetworkManager::filterReplayChat(QList<ReplayChatMessage> & replayChat) {
649     if (replayChatPartNum % REPLAY_CHAT_DEDUPE_SWAP_ITERATIONS == 0) {
650         auto oldPrev = prevChatReplayDedupeBatch;
651         prevChatReplayDedupeBatch = curChatReplayDedupeBatch;
652         oldPrev->clear();
653         curChatReplayDedupeBatch = oldPrev;
654     }
655 
656     for (auto entry = replayChat.begin(); entry != replayChat.end(); ) {
657         if (entry->deleted || curChatReplayDedupeBatch->contains(entry->id) || prevChatReplayDedupeBatch->contains(entry->id)) {
658             qDebug() << "DUPE" << entry->from << ":" << entry->id << entry->message;
659             entry = replayChat.erase(entry);
660         }
661         else {
662             //qDebug() << "GOOD" << entry->from << ":" << entry->id << entry->message;
663             curChatReplayDedupeBatch->insert(entry->id);
664             entry++;
665         }
666     }
667 
668     replayChatPartNum++;
669 }
670 
vodChatPieceReply()671 void NetworkManager::vodChatPieceReply() {
672     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
673 
674     if (lastVodChatRequest == reply) {
675         lastVodChatRequest = nullptr;
676     }
677 
678     if (!handleNetworkError(reply)) {
679         return;
680     }
681 
682     QByteArray data = reply->readAll();
683 
684     //qDebug() << data;
685 
686     ReplayChatPiece ret = JsonParser::parseVodChatPiece(data);
687 
688     filterReplayChat(ret.comments);
689 
690     emit vodChatPieceGetOperationFinished(ret);
691 
692     reply->deleteLater();
693 }
694 
695 const QString NetworkManager::CHANNEL_BADGES_URL_PREFIX = QString(KRAKEN_API) + "/chat/";
696 const QString NetworkManager::CHANNEL_BADGES_URL_SUFFIX = "/badges";
697 
getChannelBadgeUrls(const quint64 channelId)698 void NetworkManager::getChannelBadgeUrls(const quint64 channelId) {
699     QString url = CHANNEL_BADGES_URL_PREFIX + QString::number(channelId) + CHANNEL_BADGES_URL_SUFFIX;
700     QString auth = "OAuth " + access_token;
701 
702     qDebug() << "Requesting" << url;
703 
704     QNetworkRequest request;
705 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
706     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
707     request.setRawHeader("Client-ID", getClientId().toUtf8());
708     request.setUrl(QUrl(url));
709     request.setRawHeader(QString("Authorization").toUtf8(), auth.toUtf8());
710 
711     QNetworkReply *reply = operation->get(request);
712 
713     connect(reply, &QNetworkReply::finished, this, &NetworkManager::channelBadgeUrlsReply);
714 }
715 
716 const QString NetworkManager::CHANNEL_BADGES_BETA_URL_PREFIX = "https://badges.twitch.tv/v1/badges/channels/";
717 const QString NetworkManager::CHANNEL_BADGES_BETA_URL_SUFFIX = "/display?language=en";
718 const QString NetworkManager::GLOBAL_BADGES_BETA_URL = "https://badges.twitch.tv/v1/badges/global/display?language=en";
719 
getChannelBadgeUrlsBeta(const int channelID)720 void NetworkManager::getChannelBadgeUrlsBeta(const int channelID) {
721     QString url = CHANNEL_BADGES_BETA_URL_PREFIX + QString::number(channelID) + CHANNEL_BADGES_BETA_URL_SUFFIX;
722 
723     qDebug() << "Requesting" << url;
724 
725     QNetworkRequest request;
726 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
727     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
728     request.setRawHeader("Client-ID", getClientId().toUtf8());
729     request.setUrl(QUrl(url));
730 
731     QNetworkReply *reply = operation->get(request);
732 
733     connect(reply, &QNetworkReply::finished, this, &NetworkManager::channelBadgeUrlsBetaReply);
734 }
735 
getGlobalBadgesUrlsBeta()736 void NetworkManager::getGlobalBadgesUrlsBeta() {
737     QString url = GLOBAL_BADGES_BETA_URL;
738 
739     qDebug() << "Requesting" << url;
740 
741     QNetworkRequest request;
742 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
743     request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
744     request.setRawHeader("Client-ID", getClientId().toUtf8());
745     request.setUrl(QUrl(url));
746 
747     QNetworkReply *reply = operation->get(request);
748 
749     connect(reply, &QNetworkReply::finished, this, &NetworkManager::globalBadgeUrlsBetaReply);
750 }
751 
getChannelBitsUrls(const int channelID)752 void NetworkManager::getChannelBitsUrls(const int channelID) {
753     QString url = QString(KRAKEN_API) + QString("/bits/actions?channel_id=") + QString::number(channelID);
754 
755     qDebug() << "Requesting" << url;
756 
757     QNetworkRequest request;
758 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
759     request.setRawHeader("Client-ID", getClientId().toUtf8());
760     request.setRawHeader("Accept", QString("application/vnd.twitchtv.v5+json").toUtf8());
761     request.setUrl(QUrl(url));
762 
763     QNetworkReply *reply = operation->get(request);
764 
765     connect(reply, &QNetworkReply::finished, this, &NetworkManager::channelBitsUrlsReply);
766 }
767 
channelBitsUrlsReply()768 void NetworkManager::channelBitsUrlsReply() {
769     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
770 
771     if (!handleNetworkError(reply)) {
772         return;
773     }
774     QByteArray data = reply->readAll();
775 
776     QString urlString = reply->url().toString();
777 
778     qDebug() << "url was" << urlString;
779 
780     int eqPos = urlString.lastIndexOf('=');
781 
782     if (eqPos != -1) {
783         QString channelIDStr = urlString.mid(eqPos + 1);
784         int channelID = channelIDStr.toInt();
785         qDebug() << "bits urls for channel" << channelID << "loaded";
786         BitsQStringsMap urls;
787         BitsQStringsMap colors;
788         JsonParser::parseBitsData(data, urls, colors);
789 
790         emit getChannelBitsUrlsOperationFinished(channelID, urls, colors);
791     }
792     else {
793         qDebug() << "can't determine channel from request url";
794     }
795 
796     reply->deleteLater();
797 }
798 
getGlobalBitsUrls()799 void NetworkManager::getGlobalBitsUrls() {
800     QString url = QString(KRAKEN_API) + QString("/bits/actions");
801 
802     qDebug() << "Requesting" << url;
803 
804     QNetworkRequest request;
805 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
806     request.setRawHeader("Accept", QString("application/vnd.twitchtv.v5+json").toUtf8());
807     request.setRawHeader("Client-ID", getClientId().toUtf8());
808     request.setUrl(QUrl(url));
809 
810     QNetworkReply *reply = operation->get(request);
811 
812     connect(reply, &QNetworkReply::finished, this, &NetworkManager::globalBitsUrlsReply);
813 }
814 
globalBitsUrlsReply()815 void NetworkManager::globalBitsUrlsReply() {
816     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
817 
818     if (!handleNetworkError(reply)) {
819         return;
820     }
821     QByteArray data = reply->readAll();
822 
823     QString urlString = reply->url().toString();
824 
825 
826     BitsQStringsMap urls;
827     BitsQStringsMap colors;
828     JsonParser::parseBitsData(data, urls, colors);
829 
830     emit getGlobalBitsUrlsOperationFinished(urls, colors);
831 
832     reply->deleteLater();
833 }
834 
getChannelBttvEmotes(const QString channel)835 void NetworkManager::getChannelBttvEmotes(const QString channel) {
836     QString url = QString(BTTV_API) + QString("/users/twitch/") + QUrl::toPercentEncoding(channel);
837 
838     qDebug() << "Requesting" << url;
839 
840     QNetworkRequest request;
841     request.setUrl(QUrl(url));
842 
843     QNetworkReply *reply = operation->get(request);
844 
845     connect(reply, &QNetworkReply::finished, this, &NetworkManager::channelBttvEmotesReply);
846 }
847 
channelBttvEmotesReply()848 void NetworkManager::channelBttvEmotesReply() {
849     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
850 
851     if (!handleNetworkError(reply)) {
852         return;
853     }
854     QByteArray data = reply->readAll();
855 
856     auto url = reply->url();
857     QString urlString = url.toString();
858     QString channel = urlString.mid(urlString.lastIndexOf("/") + 1);
859 
860     auto emotes = JsonParser::parseBttvEmotesData(data);
861 
862     emit getChannelBttvEmotesOperationFinished(channel, emotes);
863 
864     reply->deleteLater();
865 }
866 
getGlobalBttvEmotes()867 void NetworkManager::getGlobalBttvEmotes() {
868     QString url = QString(BTTV_API) + QString("/emotes/global");
869 
870     qDebug() << "Requesting" << url;
871 
872     QNetworkRequest request;
873     request.setUrl(QUrl(url));
874 
875     QNetworkReply *reply = operation->get(request);
876 
877     connect(reply, &QNetworkReply::finished, this, &NetworkManager::globalBttvEmotesReply);
878 }
879 
globalBttvEmotesReply()880 void NetworkManager::globalBttvEmotesReply() {
881     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
882 
883     if (!handleNetworkError(reply)) {
884         return;
885     }
886     QByteArray data = reply->readAll();
887 
888     auto emotes = JsonParser::parseBttvEmotesData(data);
889 
890     emit getGlobalBttvEmotesOperationFinished(emotes);
891 
892     reply->deleteLater();
893 }
894 
editUserFavourite(const quint64 userId,const quint64 channelId,bool add)895 void NetworkManager::editUserFavourite(const quint64 userId, const quint64 channelId, bool add)
896 {
897     QString url = QString(KRAKEN_API) + "/users/" + QString::number(userId)
898             + "/follows/channels/" + QString::number(channelId);
899 
900     QString auth = "OAuth " + access_token;
901 
902     QNetworkRequest request;
903 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
904     request.setRawHeader("Accept", QString("application/vnd.twitchtv.v5+json").toUtf8());
905     request.setRawHeader("Client-ID", getClientId().toUtf8());
906     request.setUrl(QUrl(url));
907     request.setRawHeader(QString("Authorization").toUtf8(), auth.toUtf8());
908 
909     QNetworkReply *reply = 0;
910 
911     if (add)
912         reply = operation->put(request, QByteArray());
913     else
914         reply = operation->deleteResource(request);
915 
916     connect(reply, &QNetworkReply::finished, this, &NetworkManager::editUserFavouritesReply);
917 }
918 
getManager() const919 QNetworkAccessManager *NetworkManager::getManager() const
920 {
921     return operation;
922 }
923 
getM3U8Data(const QString & url,M3U8TYPE type)924 void NetworkManager::getM3U8Data(const QString &url, M3U8TYPE type)
925 {
926     QNetworkRequest request;
927 	//request.setRawHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1;  http://www.google.com/bot.html");
928     request.setRawHeader("Client-ID", getClientId().toUtf8());
929     request.setUrl(QUrl(url));
930 
931     request.setAttribute(QNetworkRequest::User, type);
932 
933     QNetworkReply *reply = operation->get(request);
934 
935     connect(reply, &QNetworkReply::finished, this, &NetworkManager::m3u8Reply);
936 }
937 
handleNetworkError(QNetworkReply * reply)938 bool NetworkManager::handleNetworkError(QNetworkReply *reply)
939 {
940     if (reply->error() != QNetworkReply::NoError){
941 
942         if (reply->error() >= 1 && reply->error() <= 199) {
943 
944             if (connectionOK == true) {
945                 connectionOK = false;
946                 emit networkAccessChanged(false);
947             }
948 
949             if (!offlinePoller.isActive())
950                 offlinePoller.start();
951         }
952 
953         qDebug() << reply->errorString();
954 
955         return false;
956     }
957 
958     if (!connectionOK) {
959         connectionOK = true;
960         emit networkAccessChanged(true);
961     }
962 
963     if (offlinePoller.isActive())
964         offlinePoller.stop();
965 
966     return true;
967 }
968 
handleSslErrors(QNetworkReply *,QList<QSslError> errors)969 void NetworkManager::handleSslErrors(QNetworkReply * /*reply*/, QList<QSslError> errors)
970 {
971     foreach (const QSslError & e, errors) {
972         qDebug() << "Ssl error: " << e.errorString();
973     }
974 
975     //reply->ignoreSslErrors(errors);
976 }
977 
streamReply()978 void NetworkManager::streamReply()
979 {
980     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
981 
982     if (!handleNetworkError(reply)) {
983         return;
984     }
985 
986     QByteArray data = reply->readAll();
987 
988     //qDebug() << data;
989 
990     Channel *channel = JsonParser::parseStream(data);
991 
992     QString channelIdStr = reply->url().toString();
993     channelIdStr.remove(0, channelIdStr.lastIndexOf('/') + 1);
994     const quint64 channelId = channelIdStr.toULongLong();
995 
996     emit streamGetOperationFinished(channelId, channel->isOnline());
997 
998     channel->deleteLater();
999 
1000     reply->deleteLater();
1001 }
1002 
addOfflineChannels(QList<Channel * > & channels,const QList<quint64> & expectedChannelIds)1003 void addOfflineChannels(QList<Channel *> & channels, const QList<quint64> & expectedChannelIds) {
1004     if (channels.count() < expectedChannelIds.count()) {
1005         QSet<quint64> unseenChannelIds = expectedChannelIds.toSet();
1006 
1007         foreach(const Channel* channel, channels) {
1008             unseenChannelIds.remove(channel->getId());
1009         }
1010         foreach(const quint64 id, unseenChannelIds) {
1011             channels.append(new Channel(id));
1012         }
1013     }
1014 }
1015 
1016 template <class U>
addULongLongStringList(U & modify,const QStringList & newItems)1017 void addULongLongStringList(U & modify, const QStringList & newItems) {
1018     modify.reserve(modify.length() + newItems.length());
1019     for (const QString & s : newItems) {
1020         modify.append(s.toULongLong());
1021     }
1022 }
1023 
allStreamsReply()1024 void NetworkManager::allStreamsReply()
1025 {
1026     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1027 
1028     if (!handleNetworkError(reply)) {
1029         return;
1030     }
1031     QByteArray data = reply->readAll();
1032 
1033     QList<quint64> queriedChannelIds;
1034 
1035     const QUrlQuery query(reply->url().query());
1036     if (query.hasQueryItem("channel")) {
1037         addULongLongStringList(queriedChannelIds, query.queryItemValue("channel").split(","));
1038     }
1039 
1040     PagedResult<Channel *> out = JsonParser::parseStreams(data);
1041 
1042     addOfflineChannels(out.items, queriedChannelIds);
1043 
1044     emit allStreamsOperationFinished(out.items);
1045 
1046     reply->deleteLater();
1047 }
1048 
searchGamesReply()1049 void NetworkManager::searchGamesReply()
1050 {
1051 
1052     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1053 
1054     if (!handleNetworkError(reply)) {
1055         return;
1056     }
1057     QByteArray data = reply->readAll();
1058 
1059     emit searchGamesOperationFinished(JsonParser::parseGames(data));
1060 
1061     reply->deleteLater();
1062 }
1063 
gamesReply()1064 void NetworkManager::gamesReply()
1065 {
1066     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1067 
1068     if (!handleNetworkError(reply)) {
1069         return;
1070     }
1071     QByteArray data = reply->readAll();
1072 
1073     emit gamesOperationFinished(JsonParser::parseGames(data));
1074 
1075     reply->deleteLater();
1076 }
1077 
gameStreamsReply()1078 void NetworkManager::gameStreamsReply()
1079 {
1080     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1081 
1082     if (!handleNetworkError(reply)) {
1083         return;
1084     }
1085     QByteArray data = reply->readAll();
1086 
1087     //qDebug() << data;
1088 
1089     QList<quint64> queriedChannelIds;
1090 
1091     const QUrlQuery query(reply->url().query());
1092     if (query.hasQueryItem("channel")) {
1093         addULongLongStringList(queriedChannelIds, query.queryItemValue("channel").split(","));
1094     }
1095 
1096     PagedResult<Channel *> out = JsonParser::parseStreams(data);
1097 
1098     addOfflineChannels(out.items, queriedChannelIds);
1099 
1100     emit gameStreamsOperationFinished(out.items, out.total);
1101 
1102     reply->deleteLater();
1103 }
1104 
featuredStreamsReply()1105 void NetworkManager::featuredStreamsReply()
1106 {
1107     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1108 
1109     if (!handleNetworkError(reply)) {
1110         return;
1111     }
1112     QByteArray data = reply->readAll();
1113 
1114     //qDebug() << data;
1115 
1116     QList<Channel *> channels = JsonParser::parseFeatured(data);
1117     emit featuredStreamsOperationFinished(channels, channels.count());
1118 
1119     reply->deleteLater();
1120 }
1121 
searchChannelsReply()1122 void NetworkManager::searchChannelsReply()
1123 {
1124     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1125 
1126     if (!handleNetworkError(reply)) {
1127         return;
1128     }
1129     QByteArray data = reply->readAll();
1130 
1131     //qDebug() << data;
1132 
1133     auto result = JsonParser::parseChannels(data);
1134     emit searchChannelsOperationFinished(result.items, result.total);
1135 
1136     reply->deleteLater();
1137 }
1138 
streamExtractReply()1139 void NetworkManager::streamExtractReply()
1140 {
1141     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1142 
1143     if (!handleNetworkError(reply)) {
1144         emit error("token_error");
1145         return;
1146     }
1147 
1148     QByteArray data = reply->readAll();
1149     //qDebug() << data;
1150 
1151     M3U8TYPE type = (M3U8TYPE) reply->request().attribute(QNetworkRequest::User).toInt();
1152 
1153     QString url;
1154 
1155     switch (type) {
1156     case LIVE:
1157         url = JsonParser::parseChannelStreamExtractionInfo(data);
1158         break;
1159 
1160     case VOD:
1161         url = JsonParser::parseVodExtractionInfo(data);
1162         break;
1163     }
1164 
1165     getM3U8Data(url, type);
1166 
1167     reply->deleteLater();
1168 }
1169 
m3u8Reply()1170 void NetworkManager::m3u8Reply()
1171 {
1172     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1173 
1174     if (!handleNetworkError(reply)) {
1175 
1176         emit error("playlist_error");
1177 
1178         return;
1179     }
1180 
1181     QByteArray data = reply->readAll();
1182 
1183     switch ((M3U8TYPE) reply->request().attribute(QNetworkRequest::User).toInt()) {
1184     case LIVE:
1185         emit m3u8OperationFinished(m3u8::getUrls(data));
1186         break;
1187 
1188     case VOD:
1189         emit m3u8OperationBFinished(m3u8::getUrls(data));
1190         break;
1191     }
1192     //qDebug() << data;
1193 
1194     reply->deleteLater();
1195 }
1196 
broadcastsReply()1197 void NetworkManager::broadcastsReply()
1198 {
1199     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1200 
1201     if (!handleNetworkError(reply)) {
1202         return;
1203     }
1204 
1205     QByteArray data = reply->readAll();
1206 
1207     emit broadcastsOperationFinished(JsonParser::parseVods(data));
1208 
1209     reply->deleteLater();
1210 }
1211 
favouritesReply()1212 void NetworkManager::favouritesReply()
1213 {
1214     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1215 
1216     if (!handleNetworkError(reply)) {
1217         return;
1218     }
1219 
1220     QByteArray data = reply->readAll();
1221 
1222     int offset = reply->request().attribute(QNetworkRequest::User).toInt();
1223 
1224     auto result = JsonParser::parseFavourites(data);
1225     emit favouritesReplyFinished(result.items, offset, result.total);
1226 
1227     reply->deleteLater();
1228 }
1229 
editUserFavouritesReply()1230 void NetworkManager::editUserFavouritesReply()
1231 {
1232     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1233 
1234     if (!handleNetworkError(reply)) {
1235         return;
1236     }
1237 
1238     //Nothing to do
1239     emit userEditFollowsOperationFinished();
1240 
1241     reply->deleteLater();
1242 }
1243 
userReply()1244 void NetworkManager::userReply()
1245 {
1246     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1247 
1248     if (!handleNetworkError(reply)) {
1249         return;
1250     }
1251     QByteArray data = reply->readAll();
1252 
1253     auto pair = JsonParser::parseUser(data);
1254     emit userOperationFinished(pair.first, pair.second);
1255 
1256     reply->deleteLater();
1257 }
1258 
emoteSetsReply()1259 void NetworkManager::emoteSetsReply()
1260 {
1261     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1262 
1263     if (!handleNetworkError(reply)) {
1264         return;
1265     }
1266     QByteArray data = reply->readAll();
1267 
1268     emit getEmoteSetsOperationFinished(JsonParser::parseEmoteSets(data));
1269 
1270     reply->deleteLater();
1271 }
1272 
channelBadgeUrlsReply()1273 void NetworkManager::channelBadgeUrlsReply()
1274 {
1275     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1276 
1277     if (!handleNetworkError(reply)) {
1278         return;
1279     }
1280     QByteArray data = reply->readAll();
1281 
1282     QString urlString = reply->url().toString();
1283 
1284     qDebug() << "url was" << urlString;
1285 
1286     if (urlString.startsWith(CHANNEL_BADGES_URL_PREFIX) && urlString.endsWith(CHANNEL_BADGES_URL_SUFFIX)) {
1287         quint64 channelId = urlString.mid(CHANNEL_BADGES_URL_PREFIX.length(), urlString.length() - CHANNEL_BADGES_URL_PREFIX.length() - CHANNEL_BADGES_URL_SUFFIX.length()).toULongLong();
1288         qDebug() << "badges for channel" << channelId << "loaded";
1289         auto badges = JsonParser::parseChannelBadgeUrls(data);
1290 
1291         emit getChannelBadgeUrlsOperationFinished(channelId, badges);
1292     }
1293     else {
1294         qDebug() << "can't determine channel from badges request url";
1295     }
1296 
1297 
1298     reply->deleteLater();
1299 }
1300 
channelBadgeUrlsBetaReply()1301 void NetworkManager::channelBadgeUrlsBetaReply()
1302 {
1303     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1304 
1305     if (!handleNetworkError(reply)) {
1306         return;
1307     }
1308     QByteArray data = reply->readAll();
1309 
1310     QString urlString = reply->url().toString();
1311 
1312     qDebug() << "url was" << urlString;
1313 
1314     if (urlString.startsWith(CHANNEL_BADGES_BETA_URL_PREFIX) && urlString.endsWith(CHANNEL_BADGES_BETA_URL_SUFFIX)) {
1315         QString channelIDStr = urlString.mid(CHANNEL_BADGES_BETA_URL_PREFIX.length(), urlString.length() - CHANNEL_BADGES_BETA_URL_PREFIX.length() - CHANNEL_BADGES_BETA_URL_SUFFIX.length());
1316         int channelID = channelIDStr.toInt();
1317         qDebug() << "beta badges for channel" << channelID << "loaded";
1318         auto badges = JsonParser::parseBadgeUrlsBetaFormat(data);
1319 
1320         emit getChannelBadgeBetaUrlsOperationFinished(channelID, badges);
1321     }
1322     else {
1323         qDebug() << "can't determine channel from badges request url";
1324     }
1325 
1326     reply->deleteLater();
1327 }
1328 
globalBadgeUrlsBetaReply()1329 void NetworkManager::globalBadgeUrlsBetaReply()
1330 {
1331     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
1332 
1333     if (!handleNetworkError(reply)) {
1334         return;
1335     }
1336     QByteArray data = reply->readAll();
1337 
1338     QString urlString = reply->url().toString();
1339 
1340     qDebug() << "url was" << urlString;
1341 
1342     qDebug() << "global beta badges loaded";
1343     auto badges = JsonParser::parseBadgeUrlsBetaFormat(data);
1344 
1345     emit getGlobalBadgeBetaUrlsOperationFinished(badges);
1346 
1347     reply->deleteLater();
1348 }
1349