Hello community, here is the log from the commit of package orion for openSUSE:Factory checked in at 2017-06-20 09:40:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/orion (Old) and /work/SRC/openSUSE:Factory/.orion.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "orion" Tue Jun 20 09:40:02 2017 rev:7 rq:504466 version:1.5.1+git~20170612 Changes: -------- --- /work/SRC/openSUSE:Factory/orion/orion.changes 2017-06-04 02:01:37.648969159 +0200 +++ /work/SRC/openSUSE:Factory/.orion.new/orion.changes 2017-06-20 09:40:04.466782845 +0200 @@ -1,0 +2,13 @@ +Tue Jun 13 13:19:06 UTC 2017 - [email protected] + +- Update to version 1.5.1+git~20170612: + * explicitly percent-encode HLS token (#175) + * VOD resume at last playback position (#177) + +------------------------------------------------------------------- +Fri Jun 09 08:50:47 UTC 2017 - [email protected] + +- Update to version 1.5.1+git~20170607: + * Debug command line option (#172) + +------------------------------------------------------------------- Old: ---- orion-1.5.1+git~20170602.tar.xz New: ---- orion-1.5.1+git~20170612.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ orion.spec ++++++ --- /var/tmp/diff_new_pack.eLe4ao/_old 2017-06-20 09:40:05.626619318 +0200 +++ /var/tmp/diff_new_pack.eLe4ao/_new 2017-06-20 09:40:05.630618754 +0200 @@ -17,7 +17,7 @@ Name: orion -Version: 1.5.1+git~20170602 +Version: 1.5.1+git~20170612 Release: 0 Summary: Twitch stream client using Qt License: GPL-3.0 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.eLe4ao/_old 2017-06-20 09:40:05.686610859 +0200 +++ /var/tmp/diff_new_pack.eLe4ao/_new 2017-06-20 09:40:05.690610296 +0200 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/alamminsalo/orion.git</param> - <param name="changesrevision">fe4787b43bf32c4795031965fade34e1f797f7f2</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">d61db488c96621d8ffdf0ddf203a12e9966e707a</param></service></servicedata> \ No newline at end of file ++++++ orion-1.5.1+git~20170602.tar.xz -> orion-1.5.1+git~20170612.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/main.cpp new/orion-1.5.1+git~20170612/src/main.cpp --- old/orion-1.5.1+git~20170602/src/main.cpp 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/main.cpp 2017-06-12 22:11:30.000000000 +0200 @@ -47,6 +47,7 @@ int main(int argc, char *argv[]) { CustomApp app(argc, argv); + app.setApplicationVersion(APP_VERSION); //Single application solution RunGuard guard("wz0dPKqHv3vX0BBsUFZt"); @@ -55,6 +56,19 @@ return -1; } + QCommandLineParser parser; + parser.setApplicationDescription("Desktop client for Twitch.tv"); + parser.addHelpOption(); + parser.addVersionOption(); + + QCommandLineOption debugOption(QStringList() << "d" << "debug", "show debug output"); + + parser.addOption(debugOption); + + parser.process(app); + + bool showDebugOutput = parser.isSet(debugOption); + //Init engine QQmlApplicationEngine engine; @@ -67,7 +81,9 @@ app.setFont(QFont(":/fonts/NotoSans-Regular.ttf", 10, QFont::Normal, false)); #ifndef QT_DEBUG - qInstallMessageHandler(noisyFailureMsgHandler); + if (!showDebugOutput) { + qInstallMessageHandler(noisyFailureMsgHandler); + } #endif app.setWindowIcon(appIcon); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/model/channelmanager.cpp new/orion-1.5.1+git~20170612/src/model/channelmanager.cpp --- old/orion-1.5.1+git~20170602/src/model/channelmanager.cpp 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/model/channelmanager.cpp 2017-06-12 22:11:30.000000000 +0200 @@ -419,6 +419,17 @@ } settings.endArray(); + int numLastPositions = settings.beginReadArray("lastPositions"); + for (int i = 0; i < numLastPositions; i++) { + settings.setArrayIndex(i); + const QString channel = settings.value("channel").toString(); + const QString vod = settings.value("vod").toString(); + const quint64 lastPosition = settings.value("position").toULongLong(); + + vodLastPlaybackPositionLoaded(channel, vod, lastPosition, i); + } + settings.endArray(); + if (settings.contains("access_token")) { setAccessToken(settings.value("access_token").toString()); } else { @@ -471,6 +482,87 @@ favouritesModel->getChannels().at(i)->writeToSettings(settings); } settings.endArray(); + + //Write last positions + int nextLastPositionEntry = settings.beginReadArray("lastPositions"); + settings.endArray(); + + settings.beginWriteArray("lastPositions"); + for (auto channelEntry = channelVodLastPositions.begin(); channelEntry != channelVodLastPositions.end(); channelEntry++) { + auto & vods = channelEntry.value(); + for (auto vodEntry = vods.begin(); vodEntry != vods.end(); vodEntry++) { + auto & lastPosition = vodEntry.value(); + if (lastPosition.modified) { + if (lastPosition.settingsIndex == -1) { + lastPosition.settingsIndex = nextLastPositionEntry++; + } + + settings.setArrayIndex(lastPosition.settingsIndex); + settings.setValue("channel", channelEntry.key()); + settings.setValue("vod", vodEntry.key()); + settings.setValue("position", vodEntry.value().lastPosition); + } + } + } + settings.endArray(); +} + +void ChannelManager::setVodLastPlaybackPosition(const QString & channel, const QString & vod, quint64 position) { + auto channelEntry = channelVodLastPositions.find(channel); + if (channelEntry == channelVodLastPositions.end()) { + channelEntry = channelVodLastPositions.insert(channel, QMap<QString, LastPosition>()); + } + + auto & vodMap = channelEntry.value(); + auto vodEntry = vodMap.find(vod); + if (vodEntry != vodMap.end()) { + vodEntry.value().lastPosition = position; + vodEntry.value().modified = true; + } + else { + // -1 index to be replaced at settings save time + vodMap.insert(vod, {position, true, -1}); + } + + emit vodLastPositionUpdated(channel, vod, position); +} + +void ChannelManager::vodLastPlaybackPositionLoaded(const QString & channel, const QString & vod, quint64 position, int settingsIndex) { + auto channelEntry = channelVodLastPositions.find(channel); + if (channelEntry == channelVodLastPositions.end()) { + channelEntry = channelVodLastPositions.insert(channel, QMap<QString, LastPosition>()); + } + + auto & vodMap = channelEntry.value(); + vodMap.remove(vod); + vodMap.insert(vod, {position, false, settingsIndex}); +} + +QVariant ChannelManager::getVodLastPlaybackPosition(const QString & channel, const QString & vod) { + auto channelEntry = channelVodLastPositions.find(channel); + if (channelEntry == channelVodLastPositions.end()) { + return QVariant(); + } + + auto & vodMap = channelEntry.value(); + auto vodEntry = vodMap.find(vod); + if (vodEntry == vodMap.end()) { + return QVariant(); + } + + return vodEntry.value().lastPosition; +} + +QVariantMap ChannelManager::getChannelVodsLastPlaybackPositions(const QString & channel) { + QVariantMap out; + auto channelEntry = channelVodLastPositions.find(channel); + if (channelEntry != channelVodLastPositions.end()) { + auto & vodMap = channelEntry.value(); + for (auto vodEntry = vodMap.constBegin(); vodEntry != vodMap.constEnd(); vodEntry++) { + out.insert(vodEntry.key(), vodEntry.value().lastPosition); + } + } + return out; } void ChannelManager::addToFavourites(const quint32 &id){ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/model/channelmanager.h new/orion-1.5.1+git~20170612/src/model/channelmanager.h --- old/orion-1.5.1+git~20170602/src/model/channelmanager.h 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/model/channelmanager.h 2017-06-12 22:11:30.000000000 +0200 @@ -31,6 +31,12 @@ class ChannelManager; +struct LastPosition { + quint64 lastPosition; + bool modified; + int settingsIndex; +}; + class BadgeImageProvider : public ImageProvider { Q_OBJECT public: @@ -126,6 +132,10 @@ static const quint32 BLOCKED_USER_LIST_FETCH_LIMIT; void getBlockedUserList(); + QMap<QString, QMap<QString, LastPosition>> channelVodLastPositions; + + void vodLastPlaybackPositionLoaded(const QString & channel, const QString & vod, quint64 position, int settingsIndex); + public: ChannelManager(NetworkManager *netman, bool hiDpi); ~ChannelManager(); @@ -187,6 +197,10 @@ Q_INVOKABLE void getVodStartTime(quint64 vodId); Q_INVOKABLE void getVodChatPiece(quint64 vodId, quint64 offset); + Q_INVOKABLE void setVodLastPlaybackPosition(const QString & channel, const QString & vod, quint64 position); + Q_INVOKABLE QVariant getVodLastPlaybackPosition(const QString & channel, const QString & vod); + Q_INVOKABLE QVariantMap getChannelVodsLastPlaybackPositions(const QString & channel); + void setSwapChat(bool value); bool getSwapChat(); void setTextScaleFactor(double value); @@ -306,6 +320,8 @@ void userBlocked(const QString & blockedUsername); void userUnblocked(const QString & unblockedUsername); + void vodLastPositionUpdated(const QString & channel, const QString & vod, const quint64 position); + public slots: void checkFavourites(); void addToFavourites(const quint32&); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/qml/PlayerView.qml new/orion-1.5.1+git~20170612/src/qml/PlayerView.qml --- old/orion-1.5.1+git~20170602/src/qml/PlayerView.qml 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/qml/PlayerView.qml 2017-06-12 22:11:30.000000000 +0200 @@ -29,6 +29,8 @@ property var streamMap property bool isVod: false property bool streamOnline: true + property string curVodId + property int lastSetPosition property bool cursorHidden: false property string currentQualityName @@ -156,18 +158,18 @@ g_cman.setQuality(streamName); } - function getStreams(channel, vod){ - getChannel(channel, vod, true); + function getStreams(channel, vod, startPos){ + getChannel(channel, vod, true, startPos); } function getChat(channel) { - getChannel(channel, null, false); + getChannel(channel, null, false, 0); if (chatview.status == 0) { chatview.status++; } } - function getChannel(channel, vod, wantVideo){ + function getChannel(channel, vod, wantVideo, startPos){ if (!channel){ return @@ -185,12 +187,14 @@ else { g_vodmgr.getBroadcasts(vod._id) isVod = true + root.curVodId = vod._id + root.lastSetPosition = startPos duration = vod.duration console.log("Setting up VOD, duration " + vod.duration) - seekBar.setPosition(0, duration) + seekBar.setPosition(startPos, duration) } } else { isVod = false; @@ -222,7 +226,7 @@ } else { var vodIdNum = parseInt(vod._id.substring(1)); console.log("replaying chat for vod", vodIdNum, "starting at", startEpochTime); - chatview.replayChat(currentChannel.name, currentChannel._id, vodIdNum, startEpochTime); + chatview.replayChat(currentChannel.name, currentChannel._id, vodIdNum, startEpochTime, startPos); } } else { chatview.joinChannel(currentChannel.name, currentChannel._id); @@ -306,8 +310,15 @@ } onPositionChanged: { - chatview.playerPositionUpdate(renderer.position); - seekBar.setPosition(renderer.position, duration) + var newPos = renderer.position; + chatview.playerPositionUpdate(newPos); + if (root.isVod) { + if (Math.abs(newPos - root.lastSetPosition) > 10) { + root.lastSetPosition = newPos; + g_cman.setVodLastPlaybackPosition(root.currentChannel.name, root.curVodId, newPos); + } + } + seekBar.setPosition(newPos, duration); } onPlayingResumed: { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/qml/VodsView.qml new/orion-1.5.1+git~20170612/src/qml/VodsView.qml --- old/orion-1.5.1+git~20170602/src/qml/VodsView.qml 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/qml/VodsView.qml 2017-06-12 22:11:30.000000000 +0200 @@ -18,9 +18,12 @@ import "util.js" as Util Item{ + id: vodsView + anchors.fill: parent property variant selectedChannel property int itemCount: 0 + property var channelVodPositions function search(channel){ @@ -41,6 +44,8 @@ header.text = "Videos for " + selectedChannel.title; + channelVodPositions = g_cman.getChannelVodsLastPlaybackPositions(channel.name); + g_vodmgr.search(selectedChannel._id, 0, 35) itemCount = 35 @@ -61,6 +66,18 @@ } } + Connections { + target: g_cman + onVodLastPositionUpdated: { + //console.log("onVodLastPositionUpdated", channel, vod, position); + if (selectedChannel.name == channel) { + channelVodPositions[vod] = position; + // need binding to update + channelVodPositions = channelVodPositions; + } + } + } + CommonGrid { id: vods tooltipEnabled: true @@ -80,17 +97,34 @@ views: model.views preview: model.preview duration: model.duration + position: channelVodPositions[model.id] || 0 game: model.game createdAt: model.createdAt } onItemClicked: { - playerView.getStreams(selectedChannel, clickedItem) + var lastPlaybackPosition = vods.getLastPlaybackPosition(selectedChannel, clickedItem); + playerView.getStreams(selectedChannel, clickedItem, lastPlaybackPosition == null? 0 : lastPlaybackPosition); + } + + function getLastPlaybackPosition(channel, vod) { + console.log("getLastPlaybackPosition", channel.name, vod._id); + return g_cman.getVodLastPlaybackPosition(channel.name, vod._id); } onItemRightClicked: { _menu.item = clickedItem - _menu.popup() + + var lastPlayed = getLastPlaybackPosition(selectedChannel, clickedItem); + var haveLastPlayTime = lastPlayed != null; + _furthestPlayedMenuItem.enabled = haveLastPlayTime; + if (haveLastPlayTime) { + _furthestPlayedMenuItem.text = "Watch video from " + Util.getTime(lastPlayed); + } else { + _furthestPlayedMenuItem.text = "Watch video from furthest played"; + } + + _menu.popup(); } onItemTooltipHover: { @@ -114,10 +148,17 @@ ContextMenu { id: _menu MenuItem { - text: "Watch video" + text: "Watch video from start" //text: "Watch;play" onTriggered: { - playerView.getStreams(selectedChannel, _menu.item) + playerView.getStreams(selectedChannel, _menu.item, 0) + } + } + + MenuItem { + id: _furthestPlayedMenuItem + onTriggered: { + playerView.getStreams(selectedChannel, _menu.item, vods.getLastPlaybackPosition(selectedChannel, _menu.item)) } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/qml/components/Video.qml new/orion-1.5.1+git~20170612/src/qml/components/Video.qml --- old/orion-1.5.1+git~20170602/src/qml/components/Video.qml 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/qml/components/Video.qml 2017-06-12 22:11:30.000000000 +0200 @@ -20,6 +20,7 @@ property string title property string preview property int views + property int position property int duration property string game property string createdAt @@ -90,6 +91,16 @@ bottom: container.bottom } } + + Rectangle { + height: infoRect.height + width: container.width * root.position / root.duration + color: Styles.purple + anchors { + left: container.left + bottom: container.bottom + } + } Text { id: channelTitle diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/qml/irc/Chat.qml new/orion-1.5.1+git~20170612/src/qml/irc/Chat.qml --- old/orion-1.5.1+git~20170602/src/qml/irc/Chat.qml 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/qml/irc/Chat.qml 2017-06-12 22:11:30.000000000 +0200 @@ -86,8 +86,8 @@ messageReceived("notice", null, "", false, false, false, [], true, "Joined channel #" + channelName, false) } - function replayChat(channelName, channelId, vodId, startEpochTime) { - chat.replay(channelName, channelId, vodId, startEpochTime, 0) + function replayChat(channelName, channelId, vodId, startEpochTime, startPos) { + chat.replay(channelName, channelId, vodId, startEpochTime, startPos) enterChannelCommon(channelName, channelId); root.replayMode = true messageReceived("notice", null, "", false, false, false, [], true, "Starting chat replay #" + channelName + " v" + vodId, false) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/qml/irc/ChatView.qml new/orion-1.5.1+git~20170612/src/qml/irc/ChatView.qml --- old/orion-1.5.1+git~20170602/src/qml/irc/ChatView.qml 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/qml/irc/ChatView.qml 2017-06-12 22:11:30.000000000 +0200 @@ -79,11 +79,11 @@ chat.leaveChannel() } - function replayChat(channelName, channelId, vodId, startEpochTime) { + function replayChat(channelName, channelId, vodId, startEpochTime, startPos) { viewerListEnabled = false; cleanupPrevChannel() chat.leaveChannel() - chat.replayChat(channelName, channelId, vodId, startEpochTime); + chat.replayChat(channelName, channelId, vodId, startEpochTime, startPos); } function playerSeek(newOffset) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/orion-1.5.1+git~20170602/src/util/jsonparser.cpp new/orion-1.5.1+git~20170612/src/util/jsonparser.cpp --- old/orion-1.5.1+git~20170602/src/util/jsonparser.cpp 2017-06-02 07:42:07.000000000 +0200 +++ new/orion-1.5.1+git~20170612/src/util/jsonparser.cpp 2017-06-12 22:11:30.000000000 +0200 @@ -358,7 +358,7 @@ url = QString("http://usher.twitch.tv/api/channel/hls/%1").arg(channel + QString(".m3u8")) + QString("?player=twitchweb") - + QString("&token=%1").arg(tokenData) + + QString("&token=") + QUrl::toPercentEncoding(tokenData) + QString("&sig=%1").arg(sig) + QString("&allow_source=true&$allow_audio_only=true"); }
