Hello community,

here is the log from the commit of package orion for openSUSE:Factory checked 
in at 2017-10-17 01:53:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/orion (Old)
 and      /work/SRC/openSUSE:Factory/.orion.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "orion"

Tue Oct 17 01:53:54 2017 rev:12 rq:534220 version:1.6.1+git~20171007

Changes:
--------
--- /work/SRC/openSUSE:Factory/orion/orion.changes      2017-09-09 
20:26:52.147262606 +0200
+++ /work/SRC/openSUSE:Factory/.orion.new/orion.changes 2017-10-17 
01:53:57.670378785 +0200
@@ -1,0 +2,13 @@
+Mon Oct 09 13:30:14 UTC 2017 - pousadua...@gmail.com
+
+- Update to version 1.6.1+git~20171007:
+  * first cut of new chat replay
+  * avoid a loop requesting chat for the same timestamp if there's a problem
+
+-------------------------------------------------------------------
+Fri Sep 22 12:24:28 UTC 2017 - pousadua...@gmail.com
+
+- Update to version 1.6.1+git~20170920:
+  * Fix desktop entry: don't hardcode icon path and correct category
+
+-------------------------------------------------------------------

Old:
----
  orion-1.6.1+git~20170827.tar.xz

New:
----
  orion-1.6.1+git~20171007.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ orion.spec ++++++
--- /var/tmp/diff_new_pack.2S2jLt/_old  2017-10-17 01:53:58.494340195 +0200
+++ /var/tmp/diff_new_pack.2S2jLt/_new  2017-10-17 01:53:58.498340007 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           orion
-Version:        1.6.1+git~20170827
+Version:        1.6.1+git~20171007
 Release:        0
 Summary:        Twitch stream client using Qt
 License:        GPL-3.0
@@ -55,10 +55,8 @@
 sed -i 's|v$$VERSION|v%{version}-%{release}|g' orion.pro
 #fix paths
 sed -i 's|path = /usr/local/share/|path = /usr/share/|g' orion.pro
-#update icon path in .desktop file
-sed -i 's|Icon=/usr/local/share/icons/orion.svg|Icon=orion|g' 
distfiles/Orion.desktop
 #fix categories in .desktop file
-sed -i 's|Categories=Games|Categories=Network;FileTransfer;|g' 
distfiles/Orion.desktop
+sed -i 's|Categories=Game|Categories=Network;FileTransfer;|g' 
distfiles/Orion.desktop
 
 %build
 qmake-qt5 QMAKE_CFLAGS+="%optflags" QMAKE_CXXFLAGS+="%optflags" 
QMAKE_STRIP="/bin/true"
@@ -80,13 +78,9 @@
 
 %files
 %defattr(-,root,root)
-#Fix SLE_12_SP2 and LEAP 42.1 builds
-%if 0%{?sle_version} <= 120200 && 0%{?suse_version} <= 1320
-%doc README.md COPYING LICENSE.txt
-%else
 %doc README.md
 %license COPYING LICENSE.txt
-%endif
+
 %attr(755,root,root) %{_bindir}/%{name}
 %{_datadir}/applications/Orion.desktop
 %{_datadir}/icons/hicolor/scalable/apps/%{name}.svg

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.2S2jLt/_old  2017-10-17 01:53:58.542337947 +0200
+++ /var/tmp/diff_new_pack.2S2jLt/_new  2017-10-17 01:53:58.542337947 +0200
@@ -1,4 +1,4 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/alamminsalo/orion.git</param>
-              <param 
name="changesrevision">594c474a49b16a8ab2baf019a6d24c8d5862316c</param></service></servicedata>
\ No newline at end of file
+              <param 
name="changesrevision">0a5ccf34b68447648fb4fad5901bf1f47f89cc6e</param></service></servicedata>
\ No newline at end of file

++++++ orion-1.6.1+git~20170827.tar.xz -> orion-1.6.1+git~20171007.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/distfiles/Orion.desktop 
new/orion-1.6.1+git~20171007/distfiles/Orion.desktop
--- old/orion-1.6.1+git~20170827/distfiles/Orion.desktop        2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/distfiles/Orion.desktop        2017-10-07 
17:56:22.000000000 +0200
@@ -2,9 +2,9 @@
 Version=1.0
 Type=Application
 Name=Orion
-Icon=/usr/local/share/icons/orion.svg
+Icon=orion
 Exec=/usr/bin/orion
 Comment=Seek and watch streams on Twitch
-Categories=Games
+Categories=Game
 Terminal=false
 StartupWMClass=orion
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/model/ircchat.cpp 
new/orion-1.6.1+git~20171007/src/model/ircchat.cpp
--- old/orion-1.6.1+git~20170827/src/model/ircchat.cpp  2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/model/ircchat.cpp  2017-10-07 
17:56:22.000000000 +0200
@@ -122,7 +122,6 @@
     _bitsProvider = BadgeContainer::getInstance()->getBitsImageProvider();
     connect(_badgeProvider, &ImageProvider::downloadComplete, this, 
&IrcChat::handleDownloadComplete);
     connect(_bitsProvider, &ImageProvider::downloadComplete, this, 
&IrcChat::handleDownloadComplete);
-    connect(VodManager::getInstance(), 
&VodManager::vodStartGetOperationFinished, this, &IrcChat::handleVodStartTime);
     connect(NetworkManager::getInstance(), 
&NetworkManager::vodChatPieceGetOperationFinished, this, 
&IrcChat::handleDownloadedReplayChat);
     connect(BadgeContainer::getInstance(), 
&BadgeContainer::channelBitsUrlsLoaded, this, 
&IrcChat::handleChannelBitsUrlsLoaded);
     connect(BadgeContainer::getInstance(), 
&BadgeContainer::channelBttvEmotesLoaded, this, 
&IrcChat::handleChannelBttvEmotesLoaded);
@@ -187,18 +186,14 @@
 
     replayVodId = vodId;
 
-    replayChatVodStartTime = vodStartEpochTime;
+    replayChatVodStartTime = 0;
 
     replayChatCurrentSeekOffset = playbackOffset;
 
     replayChatFirstLoadAfterSeek = true;
 
     // Get start timestamp of the chat replay segments as the requests need to 
be aligned to chunk boundaries from this start
-    NetworkManager::getInstance()->getVodStartTime(vodId);
-}
-
-void IrcChat::handleVodStartTime(double startTime) {
-    replayChatFirstChunkTime = startTime;
+    replayChatFirstChunkTime = 0;
     replaySeek(replayChatCurrentSeekOffset);
 }
 
@@ -207,8 +202,6 @@
     return start + qRound(qFloor(rel / multiple) * multiple);
 }
 
-const double CHAT_CHUNK_TIME = 30.0;
-
 // When seeking, request past chat starting this far back in seconds...
 const double SEEK_HISTORY_TIME = 90.0;
 // ... and display up to this many messages of it.
@@ -220,13 +213,13 @@
         VodManager::getInstance()->cancelLastVodChatRequest();
     }
     VodManager::getInstance()->resetVodChat();
+    nextChatCursor.clear();
     replayChatFirstLoadAfterSeek = true;
     replayChatRequestInProgress = true;
     // we save the offset to the point we want to start loading chat at as the 
current time
     replayChatCurrentTime = replayChatVodStartTime + qMax(newOffset - 
SEEK_HISTORY_TIME, 0.0);
-    qDebug() << "original vod playback start time" << 
QDateTime::fromMSecsSinceEpoch(replayChatCurrentTime * 1000.0, Qt::UTC);
-    nextChatChunkTimestamp = quantize(replayChatCurrentTime, 
replayChatFirstChunkTime, CHAT_CHUNK_TIME);
-    qDebug() << "quantized vod playback start time for chat chunk" << 
QDateTime::fromMSecsSinceEpoch(nextChatChunkTimestamp * 1000.0, Qt::UTC);
+    qDebug() << "original vod playback start time" << replayChatCurrentTime;
+    nextChatChunkTimestamp = replayChatCurrentTime;
     // we dump any pending messages from the previous playback position
     replayChatMessagesPending.clear();
     // we'll do an initial request for starting offset chat right now.
@@ -377,18 +370,32 @@
 
     if (!replayChatRequestInProgress && needMoreChat) {
         replayChatRequestInProgress = true;
-        VodManager::getInstance()->getVodChatPiece(replayVodId, nextChatTime);
+        if (!nextChatCursor.isEmpty()) {
+            VodManager::getInstance()->getNextVodChatPiece(replayVodId, 
nextChatCursor);
+        } else {
+            qDebug() << "No cursor from last request; falling back to 
time-based request";
+            VodManager::getInstance()->getVodChatPiece(replayVodId, 
nextChatChunkTimestamp);
+        }
     }
 }
 
-void IrcChat::handleDownloadedReplayChat(QList<ReplayChatMessage> messages) {
+void IrcChat::handleDownloadedReplayChat(ReplayChatPiece piece) {
     // eventually the initial chat response will arrive. we'll put the chat 
into a queue, and do a first chat update check
 
+    auto messages = piece.comments;
     replayChatMessagesPending.append(messages);
+    nextChatCursor = piece.next;
 
-    qDebug() << "CHAT REPLAY PART; t=" << 
QDateTime::fromMSecsSinceEpoch(nextChatChunkTimestamp * 1000.0, Qt::UTC) << 
messages.length() << "records";
-    
-    nextChatChunkTimestamp += CHAT_CHUNK_TIME;
+    qDebug() << "CHAT REPLAY PART; t=" << nextChatChunkTimestamp << 
messages.length() << "records";
+
+    // next request will start at the end of the current request
+    if (!messages.empty()) {
+        nextChatChunkTimestamp = messages.last().videoOffset / 1000.0;
+    } else {
+        // make sure we're not just going to re-request the same timestamp if 
we fall back
+        // to a time-based request
+        nextChatChunkTimestamp += 60.0;
+    }
 
     replayChatRequestInProgress = false;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/model/ircchat.h 
new/orion-1.6.1+git~20171007/src/model/ircchat.h
--- old/orion-1.6.1+git~20170827/src/model/ircchat.h    2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/model/ircchat.h    2017-10-07 
17:56:22.000000000 +0200
@@ -144,8 +144,7 @@
     void processError(QAbstractSocket::SocketError socketError);
     void processSslErrors(const QList<QSslError> &errors);
     void handleDownloadComplete();
-    void handleVodStartTime(double);
-    void handleDownloadedReplayChat(QList<ReplayChatMessage>);
+    void handleDownloadedReplayChat(ReplayChatPiece);
     void handleChannelBitsUrlsLoaded(const int channelID, BitsQStringsMap 
bitsUrls);
 
     void blockedUsersLoaded(const QSet<QString> &);
@@ -225,6 +224,7 @@
     double replayChatCurrentSeekOffset;
     double replayChatCurrentTime; // the position that playback is currently 
at in chat
     double nextChatChunkTimestamp;
+    QString nextChatCursor;
     
     quint64 replayVodId;
     QList<ReplayChatMessage> replayChatMessagesPending;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/model/vodmanager.cpp 
new/orion-1.6.1+git~20171007/src/model/vodmanager.cpp
--- old/orion-1.6.1+git~20170827/src/model/vodmanager.cpp       2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/model/vodmanager.cpp       2017-10-07 
17:56:22.000000000 +0200
@@ -24,7 +24,6 @@
 
     connect(netman, &NetworkManager::broadcastsOperationFinished, this, 
&VodManager::onSearchFinished);
     connect(netman, &NetworkManager::m3u8OperationBFinished, this, 
&VodManager::streamsGetFinished);
-    connect(netman, &NetworkManager::vodStartGetOperationFinished, this, 
&VodManager::vodStartGetOperationFinished);
     connect(netman, &NetworkManager::vodChatPieceGetOperationFinished, this, 
&VodManager::vodChatPieceGetOperationFinished);
 
     QSettings settings("orion.application", "Orion");
@@ -119,14 +118,14 @@
     netman->getBroadcastPlaybackStream(vod);
 }
 
-void VodManager::getVodStartTime(quint64 vodId) {
-    netman->getVodStartTime(vodId);
-}
-
 void VodManager::getVodChatPiece(quint64 vodId, quint64 offset) {
     netman->getVodChatPiece(vodId, offset);
 }
 
+void VodManager::getNextVodChatPiece(quint64 vodId, QString cursor) {
+    netman->getNextVodChatPiece(vodId, cursor);
+}
+
 void VodManager::cancelLastVodChatRequest() {
     netman->cancelLastVodChatRequest();
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/model/vodmanager.h 
new/orion-1.6.1+git~20171007/src/model/vodmanager.h
--- old/orion-1.6.1+git~20170827/src/model/vodmanager.h 2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/model/vodmanager.h 2017-10-07 
17:56:22.000000000 +0200
@@ -55,8 +55,8 @@
     void onSearchFinished(QList<Vod *>);
     void cancelLastVodChatRequest();
     void resetVodChat();
-    void getVodStartTime(quint64 vodId);
     void getVodChatPiece(quint64 vodId, quint64 offset);
+    void getNextVodChatPiece(quint64 vodId, QString cursor);
     void setVodLastPlaybackPosition(const QString & channel, const QString & 
vod, quint64 position);
     QVariant getVodLastPlaybackPosition(const QString & channel, const QString 
& vod);
     QVariantMap getChannelVodsLastPlaybackPositions(const QString & channel);
@@ -68,8 +68,7 @@
     void searchFinished();
     void streamsGetFinished(QVariantMap items);
 
-    void vodStartGetOperationFinished(double);
-    void vodChatPieceGetOperationFinished(QList<ReplayChatMessage>);
+    void vodChatPieceGetOperationFinished(ReplayChatPiece);
     void vodLastPositionUpdated(const QString & channel, const QString & vod, 
const quint64 position);
 
 private:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/orion-1.6.1+git~20170827/src/network/networkmanager.cpp 
new/orion-1.6.1+git~20171007/src/network/networkmanager.cpp
--- old/orion-1.6.1+git~20170827/src/network/networkmanager.cpp 2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/network/networkmanager.cpp 2017-10-07 
17:56:22.000000000 +0200
@@ -410,21 +410,6 @@
     connect(reply, &QNetworkReply::finished, this, 
&NetworkManager::emoteSetsReply);
 }
 
-void NetworkManager::getVodStartTime(quint64 vodId) {
-    QString url = QString(TWITCH_RECHAT_API) + 
QString("?start=0&video_id=v%1").arg(vodId);
-
-    qDebug() << "Failing request to get offset";
-    qDebug() << "Request" << url;
-
-    QNetworkRequest request;
-    request.setUrl(url);
-
-    QNetworkReply *reply = operation->get(request);
-
-    connect(reply, &QNetworkReply::finished, this, 
&NetworkManager::vodStartReply);
-
-}
-
 void NetworkManager::loadChatterList(const QString channel) {
     qDebug() << "Loading viewer list for" << channel;
     const QString url = QString(TWITCH_TMI_USER_API) + channel + 
QString("/chatters");
@@ -594,11 +579,13 @@
 }
 
 void NetworkManager::getVodChatPiece(quint64 vodId, quint64 offset) {
-    QString url = QString(TWITCH_RECHAT_API) + 
QString("?start=%1&video_id=v%2").arg(offset).arg(vodId);
+    QString url = QString(TWITCH_API_V5) + 
QString("/videos/%2/comments?content_offset_seconds=%1").arg(offset).arg(vodId);
 
     qDebug() << "Requesting" << url;
 
     QNetworkRequest request;
+    request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
+    request.setRawHeader("Client-ID", getClientId().toUtf8());
     request.setUrl(url);
 
     QNetworkReply *reply = operation->get(request);
@@ -608,6 +595,23 @@
     connect(reply, &QNetworkReply::finished, this, 
&NetworkManager::vodChatPieceReply);
 }
 
+void NetworkManager::getNextVodChatPiece(quint64 vodId, QString cursor) {
+    QString url = QString(TWITCH_API_V5) + 
QString("/videos/%2/comments?cursor=%1").arg(cursor).arg(vodId);
+
+    qDebug() << "Requesting" << url;
+
+    QNetworkRequest request;
+    request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
+    request.setRawHeader("Client-ID", getClientId().toUtf8());
+    request.setUrl(url);
+
+    QNetworkReply *reply = operation->get(request);
+
+    lastVodChatRequest = reply;
+
+    connect(reply, &QNetworkReply::finished, this, 
&NetworkManager::vodChatPieceReply);
+}
+
 void NetworkManager::cancelLastVodChatRequest() {
     if (lastVodChatRequest != nullptr) {
         lastVodChatRequest->abort();
@@ -654,55 +658,6 @@
     replayChatPartNum++;
 }
 
-void NetworkManager::vodStartReply() {
-    QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
-
-    if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 400) {
-        if (!handleNetworkError(reply)) {
-            return;
-        }
-    }
-
-    QByteArray data = reply->readAll();
-
-    //qDebug() << data;
-
-    QJsonParseError error;
-    QJsonDocument doc = QJsonDocument::fromJson(data, &error);
-
-    double startTime = 0.0;
-
-    if (error.error == QJsonParseError::NoError) {
-        QJsonObject json = doc.object();
-
-        //error_text = d['errors'][0]['detail']
-        QString chatReplayErrorMessage = 
json["errors"].toArray()[0].toObject()["detail"].toString();
-        // "not between (\d +) and (\d+)
-
-        const QString START_MARKER = "not between ";
-
-        int startMarkerPos = chatReplayErrorMessage.indexOf(START_MARKER);
-        if (startMarkerPos == -1) {
-            qDebug() << "chat replay error message in unexpected format:" << 
chatReplayErrorMessage;
-            return;
-        }
-
-        int timePos = startMarkerPos + START_MARKER.length();
-        QString timeStr = chatReplayErrorMessage.mid(timePos);
-        int timeEnd = timeStr.indexOf(' ');
-        if (timeEnd != -1) {
-            timeStr = timeStr.left(timeEnd);
-        }
-        qDebug() << "timeStr" << timeStr;
-        startTime = timeStr.toDouble();
-    }
-
-    emit vodStartGetOperationFinished(startTime);
-
-    reply->deleteLater();
-
-}
-
 void NetworkManager::vodChatPieceReply() {
     QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender());
 
@@ -718,9 +673,9 @@
 
     //qDebug() << data;
 
-    QList<ReplayChatMessage> ret = JsonParser::parseVodChatPiece(data);
+    ReplayChatPiece ret = JsonParser::parseVodChatPiece(data);
 
-    filterReplayChat(ret);
+    filterReplayChat(ret.comments);
 
     emit vodChatPieceGetOperationFinished(ret);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/orion-1.6.1+git~20170827/src/network/networkmanager.h 
new/orion-1.6.1+git~20171007/src/network/networkmanager.h
--- old/orion-1.6.1+git~20170827/src/network/networkmanager.h   2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/network/networkmanager.h   2017-10-07 
17:56:22.000000000 +0200
@@ -97,8 +97,8 @@
     void getChannelBttvEmotes(const QString channel);
     void getGlobalBttvEmotes();
 
-    Q_INVOKABLE void getVodStartTime(quint64 vodId);
     Q_INVOKABLE void getVodChatPiece(quint64 vodId, quint64 offset);
+    Q_INVOKABLE void getNextVodChatPiece(quint64 vodId, QString cursor);
     Q_INVOKABLE void cancelLastVodChatRequest();
     Q_INVOKABLE void resetVodChat();
     Q_INVOKABLE void loadChatterList(const QString channel);
@@ -136,8 +136,7 @@
     void getChannelBadgeBetaUrlsOperationFinished(const int, const 
QMap<QString, QMap<QString, QMap<QString, QString>>>);
     void getGlobalBadgeBetaUrlsOperationFinished(const QMap<QString, 
QMap<QString, QMap<QString, QString>>>);
 
-    void vodStartGetOperationFinished(double);
-    void vodChatPieceGetOperationFinished(QList<ReplayChatMessage>);
+    void vodChatPieceGetOperationFinished(ReplayChatPiece);
     void chatterListLoadOperationFinished(QMap<QString, QList<QString>>);
     void blockedUserListLoadOperationFinished(QList<QString>, const quint32 
nextOffset, const quint32 total);
 
@@ -174,7 +173,6 @@
     void favouritesReply();
     void editUserFavouritesReply();
     void streamReply();
-    void vodStartReply();
     void vodChatPieceReply();
     void chatterListReply();
     void blockedUserListReply();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/network/replaychat.h 
new/orion-1.6.1+git~20171007/src/network/replaychat.h
--- old/orion-1.6.1+git~20170827/src/network/replaychat.h       2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/network/replaychat.h       2017-10-07 
17:56:22.000000000 +0200
@@ -29,4 +29,10 @@
     QList<int> emoteList;
 };
 
-#endif
\ No newline at end of file
+struct ReplayChatPiece {
+    QList<ReplayChatMessage> comments;
+    QString next;
+    QString prev;
+};
+
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/network/urls.h 
new/orion-1.6.1+git~20171007/src/network/urls.h
--- old/orion-1.6.1+git~20170827/src/network/urls.h     2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/network/urls.h     2017-10-07 
17:56:22.000000000 +0200
@@ -17,10 +17,10 @@
 
 #define KRAKEN_API "https://api.twitch.tv/kraken";
 #define TWITCH_API "https://api.twitch.tv/api";
-#define TWITCH_RECHAT_API "https://rechat.twitch.tv/rechat-messages";
 #define TWITCH_TMI_USER_API "https://tmi.twitch.tv/group/user/";
 //#define TWITCH_EMOTES "http://static-cdn.jtvnw.net/emoticons/v1/";
 #define BTTV_API "https://api.betterttv.net/2";
+#define TWITCH_API_V5 "https://api.twitch.tv/v5";
 #define CLIENT_ID "0dpzlnp1w2bjlim3ldp0u96o4dq2gm"
 
 #endif // URLS_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/util/jsonparser.cpp 
new/orion-1.6.1+git~20171007/src/util/jsonparser.cpp
--- old/orion-1.6.1+git~20170827/src/util/jsonparser.cpp        2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/util/jsonparser.cpp        2017-10-07 
17:56:22.000000000 +0200
@@ -609,72 +609,138 @@
     return total;
 }
 
+int unicodeLen(const QString & text) {
+    int out = 0;
+    for (auto i = 0; i < text.length(); i++) {
+        auto ch = text.at(i);
+        if ((ch < 0xd800) || (ch > 0xdbff)) {
+            ++out;
+        }
+    }
+    return out;
+}
+
 ReplayChatMessage parseVodChatEntry(const QJsonValue &entry) {
     ReplayChatMessage out;
     
-    const QJsonObject & entryObj = entry.toObject();
+    const QJsonObject & attributes = entry.toObject();
 
-    const QJsonObject & attributes = entryObj["attributes"].toObject();
+    auto msgId = attributes["_id"].toString();
+    out.id = msgId;
 
-    out.from = attributes["from"].toString();
-    out.deleted = attributes["deleted"].toBool();
-    out.message = attributes["message"].toString();
-    out.room = attributes["room"].toString();
-    out.timestamp = attributes["timestamp"].toDouble();
-    out.videoOffset = attributes["video-offset"].toDouble();
-    out.command = attributes["command"].toString();
-
-    auto tags = attributes["tags"].toObject();
-    for (auto tagEntry = tags.constBegin(); tagEntry != tags.constEnd(); 
tagEntry++) {
-        auto tagName = tagEntry.key();
-        if (tagName == "emotes") {
-            auto emotes = tagEntry.value().toObject();
-            for (auto emoteEntry = emotes.constBegin(); emoteEntry != 
emotes.constEnd(); emoteEntry++) {
-                int emoteId = emoteEntry.key().toInt();
+    auto commenter = attributes["commenter"].toObject();
 
-                out.emoteList.append(emoteId);
+    auto name = commenter["name"].toString();
+    out.from = name;
+    QString state = attributes["state"].toString();
+    bool deleted = state != QString("published");
+    out.deleted = deleted; // XXX other types to take as valid?
+
+    auto message = attributes["message"].toObject();
+    out.message = message["body"].toString();
+    auto channelId = attributes["channel_id"].toString();
+    out.room = channelId;
+    out.videoOffset = attributes["content_offset_seconds"].toDouble() * 1000.0;
+    out.timestamp = out.videoOffset;
+
+    auto source = attributes["source"].toString();
+    if (source == QString("chat")) {
+        out.command = "PRIVMSG";
+    }
+    else {
+        // XXX need some more guesses for these
+        qDebug() << "unknown message source" << source; // XXX remove
+        out.command = "PRIVMSG";
+    }
+
+    int unicodePos = 0;
+    auto fragments = message["fragments"].toArray();
 
-                auto emotePairs = emoteEntry.value().toArray();
-                for (auto emotePair : emotePairs) {
-                    auto emotePairArray = emotePair.toArray();
-                    if (emotePairArray.size() == 2) {
-                        auto first = emotePairArray[0].toInt();
-                        auto last = emotePairArray[1].toInt();
-                        out.emotePositionsMap.insert(first, qMakePair(last, 
emoteId));
-                    }
-                }
+    QSet<int> emoteIdsSeen;
+
+    for (const auto & fragment : fragments) {
+        auto fragmentObj = fragment.toObject();
+        auto text = fragmentObj["text"].toString();
+
+        auto curTextUnicodeLen = unicodeLen(text);
+
+        if (!fragmentObj["emoticon"].isNull()) {
+            auto emoteObj = fragmentObj["emoticon"].toObject();
+
+            QString emotioconIDStr = emoteObj["emoticon_id"].toString();
+            int first = unicodePos;
+            int last = unicodePos + curTextUnicodeLen - 1; // XXX off by one?
+            int emoteId = emotioconIDStr.toInt();
+            if (emoteIdsSeen.constFind(emoteId) == emoteIdsSeen.constEnd()) {
+                emoteIdsSeen.insert(emoteId);
+                out.emoteList.append(emoteId);
             }
+            out.emotePositionsMap.insert(first, qMakePair(last, emoteId));
+        }
+
+        unicodePos += curTextUnicodeLen;
+
+    }
+    
+    // XXX tags collection stuff not hooked up yet
+    // system-msg
 
+    // 
@badges=staff/1,broadcaster/1,turbo/1;color=#008000;display-name=TWITCH_UserName;emotes=;mod=0;msg-id=resub;msg-param-months=6;room-id=1337;subscriber=1;system-msg=TWITCH_UserName\shas\ssubscribed\sfor\s6\smonths!;login=twitch_username;turbo=1;user-id=1337;user-type=staff
 :tmi.twitch.tv USERNOTICE #channel :Great stream -- keep it up!
+    QList<QString> badges;
+    for (const auto & badge : message["user_badges"].toArray()) {
+        auto badgeObj = badge.toObject();
+        auto badgeId = badgeObj["_id"].toString();
+        badges.append(badgeId + QString("/") + badgeObj["version"].toString());
+
+        // not sure if anything in the front end needs these as tags if we 
added the badges directly
+        if (badgeId == QString("moderator")) {
+            out.tags.insert("mod", true);
         }
-        else if (tagName == "mod" || tagName == "subscriber" || tagName == 
"turbo") {
-            out.tags.insert(tagName, tagEntry.value().toBool());
+
+        if (badgeId == QString("subscriber")) {
+            out.tags.insert("subscriber", true);
         }
-        else {
-            out.tags.insert(tagName, tagEntry.value().toString());
+
+        if (badgeId == QString("turbo")) {
+            out.tags.insert("turbo", true);
         }
     }
 
-    out.id = entryObj["id"].toString();
+    if (!badges.isEmpty()) {
+        out.tags.insert("badges", badges.join(","));
+    }
+
+    // most of this stuff is being populated for backward compatibility with 
things that currently operate on the tags
+    // and can be taken out if nothing uses it
+    out.tags.insert("msg-id", msgId);
+    out.tags.insert("display-name", commenter["display_name"].toString());
+    out.tags.insert("login", name);
+    out.tags.insert("color", message["user_color"].toString());
+    out.tags.insert("room-id", channelId);
+    out.tags.insert("user-id", commenter["_id"].toString());
+    out.tags.insert("user-type", commenter["type"].toString());
         
     return out;
 }
 
-QList<ReplayChatMessage> JsonParser::parseVodChatPiece(const QByteArray &data)
+ReplayChatPiece JsonParser::parseVodChatPiece(const QByteArray &data)
 {
-    QList<ReplayChatMessage> out;
+    ReplayChatPiece out;
     QJsonParseError error;
     QJsonDocument doc = QJsonDocument::fromJson(data, &error);
 
     if (error.error == QJsonParseError::NoError) {
         QJsonObject json = doc.object();
 
-        if (!json["data"].isNull() && json["data"].isArray()) {
-            const QJsonArray & chatEntries = json["data"].toArray();
+        if (!json["comments"].isNull() && json["comments"].isArray()) {
+            const QJsonArray & chatEntries = json["comments"].toArray();
             for (const auto & entry : chatEntries) {
-
-                out.append(parseVodChatEntry(entry));
+                out.comments.append(parseVodChatEntry(entry));
             }
         }
+
+        out.next = json["_next"].toString();
+        out.prev = json["_prev"].toString();
     }
 
     return out;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/orion-1.6.1+git~20170827/src/util/jsonparser.h 
new/orion-1.6.1+git~20171007/src/util/jsonparser.h
--- old/orion-1.6.1+git~20170827/src/util/jsonparser.h  2017-08-27 
10:29:37.000000000 +0200
+++ new/orion-1.6.1+git~20171007/src/util/jsonparser.h  2017-10-07 
17:56:22.000000000 +0200
@@ -60,7 +60,7 @@
     static QMap<int, QMap<int, QString>> parseEmoteSets(const QByteArray&);
     static QMap<QString, QMap<QString, QString>> parseChannelBadgeUrls(const 
QByteArray &data);
     static QMap<QString, QMap<QString, QMap<QString, QString>>> 
parseBadgeUrlsBetaFormat(const QByteArray &data);
-    static QList<ReplayChatMessage> parseVodChatPiece(const QByteArray &data);
+    static ReplayChatPiece parseVodChatPiece(const QByteArray &data);
     static QMap<QString, QList<QString>> parseChatterList(const QByteArray 
&data);
     static PagedResult<QString> parseBlockList(const QByteArray &data);
     static void parseBitsData(const QByteArray &data, QMap<QString, 
QMap<QString, QString>> & outUrls, QMap<QString, QMap<QString, QString>> & 
outColors);


Reply via email to