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 "vodmanager.h"
16 #include <QSettings>
17 #include <QCoreApplication>
18 #include <cmath>
19 
VodManager(QObject * parent)20 VodManager::VodManager(QObject *parent) :
21     QObject(parent),
22     netman(NetworkManager::getInstance())
23 {
24     qmlRegisterInterface<VodListModel>("VodListModel");
25     _model = new VodListModel(this);
26 
27     connect(netman, &NetworkManager::broadcastsOperationFinished, this, &VodManager::onSearchFinished);
28     connect(netman, &NetworkManager::m3u8OperationBFinished, this, &VodManager::streamsGetFinished);
29     connect(netman, &NetworkManager::vodChatPieceGetOperationFinished, this, &VodManager::vodChatPieceGetOperationFinished);
30 
31     QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
32     int numLastPositions = settings.beginReadArray("lastPositions");
33     for (int i = 0; i < numLastPositions; i++) {
34         settings.setArrayIndex(i);
35         const QString channel = settings.value("channel").toString();
36         const QString vod = settings.value("vod").toString();
37         const quint64 lastPosition = settings.value("position").toULongLong();
38 
39         vodLastPlaybackPositionLoaded(channel, vod, lastPosition, i);
40     }
41     settings.endArray();
42 
43     emit modelChanged();
44 }
45 
getInstance()46 VodManager *VodManager::getInstance() {
47     static VodManager *instance = new VodManager();
48     return instance;
49 }
50 
~VodManager()51 VodManager::~VodManager()
52 {
53     saveSettings();
54     delete _model;
55 }
56 
search(const quint64 channelId,const quint32 offset,const quint32 limit)57 void VodManager::search(const quint64 channelId, const quint32 offset, const quint32 limit)
58 {
59     if (offset == 0) {
60         _model->clear();
61         emit searchStarted();
62     }
63 
64     netman->getBroadcasts(channelId, offset, limit);
65 }
66 
onSearchFinished(QList<Vod * > items)67 void VodManager::onSearchFinished(QList<Vod *> items)
68 {
69     _model->addAll(items);
70 
71     qDeleteAll(items);
72     items.clear();
73 
74     emit searchFinished();
75 }
76 
getModel() const77 VodListModel *VodManager::getModel() const
78 {
79     return _model;
80 }
81 
saveSettings()82 void VodManager::saveSettings() {
83     //Save
84     QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
85 
86     //Write last positions
87     int nextLastPositionEntry = settings.beginReadArray("lastPositions");
88     settings.endArray();
89 
90     settings.beginWriteArray("lastPositions");
91     for (auto channelEntry = channelVodLastPositions.begin(); channelEntry != channelVodLastPositions.end(); channelEntry++) {
92         auto & vods = channelEntry.value();
93         for (auto vodEntry = vods.begin(); vodEntry != vods.end(); vodEntry++) {
94             auto & lastPosition = vodEntry.value();
95             if (lastPosition.modified) {
96                 if (lastPosition.settingsIndex == -1) {
97                     lastPosition.settingsIndex = nextLastPositionEntry++;
98                 }
99 
100                 settings.setArrayIndex(lastPosition.settingsIndex);
101                 settings.setValue("channel", channelEntry.key());
102                 settings.setValue("vod", vodEntry.key());
103                 settings.setValue("position", vodEntry.value().lastPosition);
104             }
105         }
106     }
107     settings.endArray();
108 
109 }
110 
getGame() const111 QString VodManager::getGame() const
112 {
113     return game;
114 }
115 
getBroadcasts(QString vod)116 void VodManager::getBroadcasts(QString vod)
117 {
118     //Remove leading NaN characters
119     vod.remove(QRegExp("[^0-9]"));
120 
121     netman->getBroadcastPlaybackStream(vod);
122 }
123 
getVodChatPiece(quint64 vodId,quint64 offset)124 void VodManager::getVodChatPiece(quint64 vodId, quint64 offset) {
125     netman->getVodChatPiece(vodId, offset);
126 }
127 
getNextVodChatPiece(quint64 vodId,QString cursor)128 void VodManager::getNextVodChatPiece(quint64 vodId, QString cursor) {
129     netman->getNextVodChatPiece(vodId, cursor);
130 }
131 
cancelLastVodChatRequest()132 void VodManager::cancelLastVodChatRequest() {
133     netman->cancelLastVodChatRequest();
134 }
135 
resetVodChat()136 void VodManager::resetVodChat() {
137     netman->resetVodChat();
138 }
139 
setVodLastPlaybackPosition(const QString & channel,const QString & vod,quint64 position)140 void VodManager::setVodLastPlaybackPosition(const QString & channel, const QString & vod, quint64 position) {
141     auto channelEntry = channelVodLastPositions.find(channel);
142     if (channelEntry == channelVodLastPositions.end()) {
143         channelEntry = channelVodLastPositions.insert(channel, QMap<QString, LastPosition>());
144     }
145 
146     auto & vodMap = channelEntry.value();
147     auto vodEntry = vodMap.find(vod);
148     if (vodEntry != vodMap.end()) {
149         vodEntry.value().lastPosition = position;
150         vodEntry.value().modified = true;
151         if (std::fabs(static_cast<double>(vodEntry.value().lastPosition) - static_cast<double>(position)) >= 10) {
152             saveSettings();
153         }
154     } else {
155         // -1 index to be replaced at settings save time
156         vodMap.insert(vod, {position, true, -1});
157         saveSettings();
158     }
159 
160     emit vodLastPositionUpdated(channel, vod, position);
161 }
162 
vodLastPlaybackPositionLoaded(const QString & channel,const QString & vod,quint64 position,int settingsIndex)163 void VodManager::vodLastPlaybackPositionLoaded(const QString & channel, const QString & vod, quint64 position, int settingsIndex) {
164     auto channelEntry = channelVodLastPositions.find(channel);
165     if (channelEntry == channelVodLastPositions.end()) {
166         channelEntry = channelVodLastPositions.insert(channel, QMap<QString, LastPosition>());
167     }
168 
169     auto & vodMap = channelEntry.value();
170     vodMap.remove(vod);
171     vodMap.insert(vod, {position, false, settingsIndex});
172 }
173 
getVodLastPlaybackPosition(const QString & channel,const QString & vod)174 QVariant VodManager::getVodLastPlaybackPosition(const QString & channel, const QString & vod) {
175     auto channelEntry = channelVodLastPositions.find(channel);
176     if (channelEntry == channelVodLastPositions.end()) {
177         return QVariant();
178     }
179 
180     auto & vodMap = channelEntry.value();
181     auto vodEntry = vodMap.find(vod);
182     if (vodEntry == vodMap.end()) {
183         return QVariant();
184     }
185 
186     return vodEntry.value().lastPosition;
187 }
188 
getChannelVodsLastPlaybackPositions(const QString & channel)189 QVariantMap VodManager::getChannelVodsLastPlaybackPositions(const QString & channel) {
190     QVariantMap out;
191     auto channelEntry = channelVodLastPositions.find(channel);
192     if (channelEntry != channelVodLastPositions.end()) {
193         auto & vodMap = channelEntry.value();
194         for (auto vodEntry = vodMap.constBegin(); vodEntry != vodMap.constEnd(); vodEntry++) {
195             out.insert(vodEntry.key(), vodEntry.value().lastPosition);
196         }
197     }
198     return out;
199 }
200