]> git.sur5r.net Git - minitube/blob - src/ytuser.cpp
Merge tag 'upstream/2.3'
[minitube] / src / ytuser.cpp
1 /* $BEGIN_LICENSE
2
3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
5
6 Minitube 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 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
18
19 $END_LICENSE */
20
21 #include "ytuser.h"
22 #include "networkaccess.h"
23 #include "database.h"
24 #include <QtSql>
25
26 namespace The {
27 NetworkAccess* http();
28 }
29
30 YTUser::YTUser(QString userId, QObject *parent) : QObject(parent),
31     id(0),
32     userId(userId),
33     loadingThumbnail(false),
34     notifyCount(0),
35     checked(0),
36     watched(0),
37     loaded(0),
38     loading(false) { }
39
40 QHash<QString, YTUser*> YTUser::cache;
41
42 YTUser* YTUser::forId(QString userId) {
43     if (userId.isEmpty()) return 0;
44
45     if (cache.contains(userId))
46         return cache.value(userId);
47
48     QSqlDatabase db = Database::instance().getConnection();
49     QSqlQuery query(db);
50     query.prepare("select id,name,description,thumb_url,notify_count,watched,checked,loaded "
51                   "from subscriptions where user_id=?");
52     query.bindValue(0, userId);
53     bool success = query.exec();
54     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
55
56     YTUser* user = 0;
57     if (query.next()) {
58         user = new YTUser(userId);
59         user->id = query.value(0).toInt();
60         user->displayName = query.value(1).toString();
61         user->description = query.value(2).toString();
62         user->thumbnailUrl = query.value(3).toString();
63         user->notifyCount = query.value(4).toInt();
64         user->watched = query.value(5).toUInt();
65         user->checked = query.value(6).toUInt();
66         user->loaded = query.value(7).toUInt();
67         user->thumbnail = QPixmap(user->getThumbnailLocation());
68         user->maybeLoadfromAPI();
69         cache.insert(userId, user);
70     }
71
72     return user;
73 }
74
75 void YTUser::maybeLoadfromAPI() {
76     if (loading) return;
77     if (userId.isEmpty()) return;
78
79     uint now = QDateTime::currentDateTime().toTime_t();
80     static const int refreshInterval = 60 * 60 * 24 * 10;
81     if (loaded > now - refreshInterval) return;
82
83     loading = true;
84
85     QUrl url("http://gdata.youtube.com/feeds/api/users/" + userId);
86 #if QT_VERSION >= 0x050000
87     {
88         QUrl &u = url;
89         QUrlQuery url;
90 #endif
91         url.addQueryItem("v", "2");
92 #if QT_VERSION >= 0x050000
93         u.setQuery(url);
94     }
95 #endif
96     QObject *reply = The::http()->get(url);
97     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResponse(QByteArray)));
98     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
99 }
100
101 void YTUser::parseResponse(QByteArray bytes) {
102     QXmlStreamReader xml(bytes);
103     xml.readNextStartElement();
104     if (xml.name() == QLatin1String("entry"))
105         while(xml.readNextStartElement()) {
106             const QStringRef n = xml.name();
107             if (n == QLatin1String("summary"))
108                 description = xml.readElementText().simplified();
109             else if (n == QLatin1String("title"))
110                 displayName = xml.readElementText();
111             else if (n == QLatin1String("thumbnail")) {
112                 thumbnailUrl = xml.attributes().value("url").toString();
113                 xml.skipCurrentElement();
114             } else if (n == QLatin1String("username"))
115                 userName = xml.readElementText();
116             else xml.skipCurrentElement();
117         }
118
119     if (xml.hasError()) {
120         emit error(xml.errorString());
121         qWarning() << xml.errorString();
122     }
123
124     emit infoLoaded();
125     storeInfo();
126     loading = false;
127 }
128
129 void YTUser::loadThumbnail() {
130     if (loadingThumbnail) return;
131     if (thumbnailUrl.isEmpty()) return;
132     loadingThumbnail = true;
133
134 #ifdef Q_OS_WIN
135     thumbnailUrl.replace(QLatin1String("https://"), QLatin1String("http://"));
136 #endif
137
138     QUrl url(thumbnailUrl);
139     QObject *reply = The::http()->get(url);
140     connect(reply, SIGNAL(data(QByteArray)), SLOT(storeThumbnail(QByteArray)));
141     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
142 }
143
144 const QString & YTUser::getThumbnailDir() {
145     static const QString thumbDir =
146         #if QT_VERSION >= 0x050000
147             QStandardPaths::writableLocation(QStandardPaths::DataLocation)
148         #else
149             QDesktopServices::storageLocation(QDesktopServices::DataLocation)
150         #endif
151             + "/channels/";
152     return thumbDir;
153 }
154
155 QString YTUser::getThumbnailLocation() {
156     return getThumbnailDir() + userId;
157 }
158
159 void YTUser::unsubscribe() {
160     YTUser::unsubscribe(userId);
161 }
162
163 void YTUser::storeThumbnail(QByteArray bytes) {
164     thumbnail.loadFromData(bytes);
165     static const int maxWidth = 88;
166
167     QDir dir;
168     dir.mkpath(getThumbnailDir());
169
170     if (thumbnail.width() > maxWidth) {
171         thumbnail = thumbnail.scaledToWidth(maxWidth, Qt::SmoothTransformation);
172         thumbnail.save(getThumbnailLocation(), "JPG");
173     } else {
174         QFile file(getThumbnailLocation());
175         if (!file.open(QIODevice::WriteOnly))
176             qWarning() << "Error opening file for writing" << file.fileName();
177         QDataStream stream(&file);
178         stream.writeRawData(bytes.constData(), bytes.size());
179     }
180
181     emit thumbnailLoaded();
182     loadingThumbnail = false;
183 }
184
185 void YTUser::requestError(QNetworkReply *reply) {
186     emit error(reply->errorString());
187     qWarning() << reply->errorString();
188     loading = false;
189     loadingThumbnail = false;
190 }
191
192 void YTUser::storeInfo() {
193     if (userId.isEmpty()) return;
194     QSqlDatabase db = Database::instance().getConnection();
195     QSqlQuery query(db);
196     query.prepare("update subscriptions set "
197                   "user_name=?, name=?, description=?, thumb_url=?, loaded=? "
198                   "where user_id=?");
199     qDebug() << userName;
200     query.bindValue(0, userName);
201     query.bindValue(1, displayName);
202     query.bindValue(2, description);
203     query.bindValue(3, thumbnailUrl);
204     query.bindValue(4, QDateTime::currentDateTime().toTime_t());
205     query.bindValue(5, userId);
206     bool success = query.exec();
207     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
208
209     loadThumbnail();
210 }
211
212 void YTUser::subscribe(QString userId) {
213     if (userId.isEmpty()) return;
214
215     uint now = QDateTime::currentDateTime().toTime_t();
216
217     QSqlDatabase db = Database::instance().getConnection();
218     QSqlQuery query(db);
219     query.prepare("insert into subscriptions "
220                   "(user_id,added,watched,checked,views,notify_count)"
221                   " values (?,?,?,0,0,0)");
222     query.bindValue(0, userId);
223     query.bindValue(1, now);
224     query.bindValue(2, now);
225     bool success = query.exec();
226     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
227
228     // This will call maybeLoadFromApi
229     YTUser::forId(userId);
230 }
231
232 void YTUser::unsubscribe(QString userId) {
233     if (userId.isEmpty()) return;
234     QSqlDatabase db = Database::instance().getConnection();
235     QSqlQuery query(db);
236     query.prepare("delete from subscriptions where user_id=?");
237     query.bindValue(0, userId);
238     bool success = query.exec();
239     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
240
241     query = QSqlQuery(db);
242     query.prepare("delete from subscriptions_videos where user_id=?");
243     query.bindValue(0, userId);
244     success = query.exec();
245     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
246
247     YTUser *user = cache.take(userId);
248     if (user) user->deleteLater();
249 }
250
251 bool YTUser::isSubscribed(QString userId) {
252     if (!Database::exists()) return false;
253     if (userId.isEmpty()) return false;
254     QSqlDatabase db = Database::instance().getConnection();
255     QSqlQuery query(db);
256     query.prepare("select count(*) from subscriptions where user_id=?");
257     query.bindValue(0, userId);
258     bool success = query.exec();
259     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
260     if (query.next())
261         return query.value(0).toInt() > 0;
262     return false;
263 }
264
265 void YTUser::updateChecked() {
266     if (userId.isEmpty()) return;
267
268     uint now = QDateTime::currentDateTime().toTime_t();
269     checked = now;
270
271     QSqlDatabase db = Database::instance().getConnection();
272     QSqlQuery query(db);
273     query.prepare("update subscriptions set checked=? where user_id=?");
274     query.bindValue(0, QDateTime::currentDateTime().toTime_t());
275     query.bindValue(1, userId);
276     bool success = query.exec();
277     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
278 }
279
280 void YTUser::updateWatched() {
281     if (userId.isEmpty()) return;
282
283     uint now = QDateTime::currentDateTime().toTime_t();
284     watched = now;
285     notifyCount = 0;
286     emit notifyCountChanged();
287
288     QSqlDatabase db = Database::instance().getConnection();
289     QSqlQuery query(db);
290     query.prepare("update subscriptions set watched=?, notify_count=0, views=views+1 where user_id=?");
291     query.bindValue(0, now);
292     query.bindValue(1, userId);
293     bool success = query.exec();
294     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
295 }
296
297 void YTUser::storeNotifyCount(int count) {
298     if (notifyCount != count)
299         emit notifyCountChanged();
300     notifyCount = count;
301
302     QSqlDatabase db = Database::instance().getConnection();
303     QSqlQuery query(db);
304     query.prepare("update subscriptions set notify_count=? where user_id=?");
305     query.bindValue(0, count);
306     query.bindValue(1, userId);
307     bool success = query.exec();
308     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
309 }
310
311 bool YTUser::updateNotifyCount() {
312     QSqlDatabase db = Database::instance().getConnection();
313     QSqlQuery query(db);
314     query.prepare("select count(*) from subscriptions_videos "
315                   "where channel_id=? and added>? and published>? and watched=0");
316     query.bindValue(0, id);
317     query.bindValue(1, watched);
318     query.bindValue(2, watched);
319     bool success = query.exec();
320     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
321     if (!query.next()) {
322         qWarning() << __PRETTY_FUNCTION__ << "Count failed";
323         return false;
324     }
325     int count = query.value(0).toInt();
326     storeNotifyCount(count);
327     return count != notifyCount;
328 }