]> git.sur5r.net Git - minitube/blob - src/channelaggregator.cpp
Imported Upstream version 2.4
[minitube] / src / channelaggregator.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 "channelaggregator.h"
22 #include "ytchannel.h"
23 #include "ytsearch.h"
24 #include "searchparams.h"
25 #include "database.h"
26 #include "video.h"
27 #ifdef APP_MAC
28 #include "macutils.h"
29 #endif
30
31 ChannelAggregator::ChannelAggregator(QObject *parent) : QObject(parent),
32     unwatchedCount(-1),
33     running(false),
34     stopped(false) {
35     checkInterval = 3600;
36
37     timer = new QTimer(this);
38     timer->setInterval(60000 * 5);
39     connect(timer, SIGNAL(timeout()), SLOT(run()));
40 }
41
42 ChannelAggregator* ChannelAggregator::instance() {
43     static ChannelAggregator* i = new ChannelAggregator();
44     return i;
45 }
46
47 void ChannelAggregator::start() {
48     stopped = false;
49     updateUnwatchedCount();
50     QTimer::singleShot(0, this, SLOT(run()));
51     if (!timer->isActive()) timer->start();
52 }
53
54 void ChannelAggregator::stop() {
55     timer->stop();
56     stopped = true;
57 }
58
59 YTChannel* ChannelAggregator::getChannelToCheck() {
60     if (stopped) return 0;
61     QSqlDatabase db = Database::instance().getConnection();
62     QSqlQuery query(db);
63     query.prepare("select user_id from subscriptions where checked<? "
64                   "order by checked limit 1");
65     query.bindValue(0, QDateTime::currentDateTimeUtc().toTime_t() - checkInterval);
66     bool success = query.exec();
67     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
68     if (query.next())
69         return YTChannel::forId(query.value(0).toString());
70     return 0;
71 }
72
73 void ChannelAggregator::run() {
74     if (running) return;
75     if (!Database::exists()) return;
76     running = true;
77     newVideoCount = 0;
78     updatedChannels.clear();
79
80     if (!Database::instance().getConnection().transaction())
81         qWarning() << "Transaction failed" << __PRETTY_FUNCTION__;
82
83     processNextChannel();
84 }
85
86 void ChannelAggregator::processNextChannel() {
87     if (stopped) {
88         running = false;
89         return;
90     }
91     qApp->processEvents();
92     YTChannel* channel = getChannelToCheck();
93     if (channel) {
94         SearchParams *params = new SearchParams();
95         params->setChannelId(channel->getChannelId());
96         params->setSortBy(SearchParams::SortByNewest);
97         params->setTransient(true);
98         params->setPublishedAfter(channel->getChecked());
99         YTSearch *videoSource = new YTSearch(params, this);
100         connect(videoSource, SIGNAL(gotVideos(QList<Video*>)), SLOT(videosLoaded(QList<Video*>)));
101         videoSource->loadVideos(50, 1);
102         channel->updateChecked();
103     } else finish();
104 }
105
106 void ChannelAggregator::finish() {
107     /*
108     foreach (YTChannel *channel, updatedChannels)
109         if (channel->updateNotifyCount())
110             emit channelChanged(channel);
111     updateUnwatchedCount();
112     */
113
114     QSqlDatabase db = Database::instance().getConnection();
115     if (!db.commit())
116         qWarning() << "Commit failed" << __PRETTY_FUNCTION__;
117
118     /*
119     QByteArray b = db.databaseName().right(20).toLocal8Bit();
120     const char* s = b.constData();
121     const int l = strlen(s);
122     int t = 1;
123     for (int i = 0; i < l; i++)
124         t += t % 2 ? s[i] / l : s[i] / t;
125     if (t != s[0]) return;
126     */
127
128 #ifdef Q_OS_MAC
129     if (newVideoCount > 0 && unwatchedCount > 0 && mac::canNotify()) {
130         QString channelNames;
131         const int total = updatedChannels.size();
132         for (int i = 0; i < total; ++i) {
133             YTChannel *channel = updatedChannels.at(i);
134             channelNames += channel->getDisplayName();
135             if (i < total-1) channelNames.append(", ");
136         }
137         channelNames = tr("By %1").arg(channelNames);
138         int actualNewVideoCount = qMin(newVideoCount, unwatchedCount);
139         mac::notify(tr("You have %n new video(s)", "", actualNewVideoCount),
140                     channelNames, QString());
141     }
142 #endif
143
144     running = false;
145 }
146
147 void ChannelAggregator::videosLoaded(const QList<Video*> &videos) {
148     sender()->deleteLater();
149
150     foreach (Video* video, videos) {
151         addVideo(video);
152         qApp->processEvents();
153     }
154
155     if (!videos.isEmpty()) {
156         YTChannel *channel = YTChannel::forId(videos.first()->channelId());
157         channel->updateNotifyCount();
158         emit channelChanged(channel);
159         updateUnwatchedCount();
160         foreach (Video* video, videos) video->deleteLater();
161     }
162
163     QTimer::singleShot(1000, this, SLOT(processNextChannel()));
164 }
165
166 void ChannelAggregator::updateUnwatchedCount() {
167     if (!Database::exists()) return;
168     QSqlDatabase db = Database::instance().getConnection();
169     QSqlQuery query(db);
170     query.prepare("select sum(notify_count) from subscriptions");
171     bool success = query.exec();
172     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
173     if (!query.next()) return;
174     int newUnwatchedCount = query.value(0).toInt();
175     if (newUnwatchedCount != unwatchedCount) {
176         unwatchedCount = newUnwatchedCount;
177         emit unwatchedCountChanged(unwatchedCount);
178     }
179 }
180
181 void ChannelAggregator::addVideo(Video *video) {
182     QSqlDatabase db = Database::instance().getConnection();
183
184     QSqlQuery query(db);
185     query.prepare("select count(*) from subscriptions_videos where video_id=?");
186     query.bindValue(0, video->id());
187     bool success = query.exec();
188     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
189     if (!query.next()) return;
190     int count = query.value(0).toInt();
191     if (count > 0) return;
192
193     // qDebug() << "Inserting" << video->author() << video->title();
194
195     YTChannel *channel = YTChannel::forId(video->channelId());
196     if (!channel) {
197         qWarning() << "channelId not present in db" << video->channelId() << video->channelTitle();
198         return;
199     }
200
201     if (!updatedChannels.contains(channel))
202         updatedChannels << channel;
203
204     uint now = QDateTime::currentDateTimeUtc().toTime_t();
205     uint published = video->published().toTime_t();
206     if (published > now) {
207         qDebug() << "fixing publish time";
208         published = now;
209     }
210
211     query = QSqlQuery(db);
212     query.prepare("insert into subscriptions_videos "
213                   "(video_id,channel_id,published,added,watched,"
214                   "title,author,user_id,description,url,thumb_url,views,duration) "
215                   "values (?,?,?,?,?,?,?,?,?,?,?,?,?)");
216     query.bindValue(0, video->id());
217     query.bindValue(1, channel->getId());
218     query.bindValue(2, published);
219     query.bindValue(3, now);
220     query.bindValue(4, 0);
221     query.bindValue(5, video->title());
222     query.bindValue(6, video->channelTitle());
223     query.bindValue(7, video->channelId());
224     query.bindValue(8, video->description());
225     query.bindValue(9, video->webpage());
226     query.bindValue(10, video->thumbnailUrl());
227     query.bindValue(11, video->viewCount());
228     query.bindValue(12, video->duration());
229     success = query.exec();
230     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
231
232     newVideoCount++;
233
234     query = QSqlQuery(db);
235     query.prepare("update subscriptions set updated=? where user_id=?");
236     query.bindValue(0, published);
237     query.bindValue(1, channel->getChannelId());
238     success = query.exec();
239     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
240 }
241
242 void ChannelAggregator::markAllAsWatched() {
243     uint now = QDateTime::currentDateTimeUtc().toTime_t();
244
245     QSqlDatabase db = Database::instance().getConnection();
246     QSqlQuery query(db);
247     query.prepare("update subscriptions set watched=?, notify_count=0");
248     query.bindValue(0, now);
249     bool success = query.exec();
250     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
251     unwatchedCount = 0;
252
253     foreach (YTChannel *channel, YTChannel::getCachedChannels()) {
254         channel->setWatched(now);
255         channel->setNotifyCount(0);
256     }
257
258     emit unwatchedCountChanged(0);
259 }
260
261 void ChannelAggregator::videoWatched(Video *video) {
262     if (!Database::exists()) return;
263     QSqlDatabase db = Database::instance().getConnection();
264     QSqlQuery query(db);
265     query.prepare("update subscriptions_videos set watched=? where video_id=?");
266     query.bindValue(0, QDateTime::currentDateTimeUtc().toTime_t());
267     query.bindValue(1, video->id());
268     bool success = query.exec();
269     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
270     if (query.numRowsAffected() > 0) {
271         YTChannel *channel = YTChannel::forId(video->channelId());
272         channel->updateNotifyCount();
273     }
274 }
275
276 void ChannelAggregator::cleanup() {
277     const int maxVideos = 1000;
278     const int maxDeletions = 1000;
279     if (!Database::exists()) return;
280     QSqlDatabase db = Database::instance().getConnection();
281
282     QSqlQuery query(db);
283     bool success = query.exec("select count(*) from subscriptions_videos");
284     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
285     if (!query.next()) return;
286     int count = query.value(0).toInt();
287     if (count <= maxVideos) return;
288
289     query = QSqlQuery(db);
290     query.prepare("delete from subscriptions_videos where id in "
291                   "(select id from subscriptions_videos "
292                   "order by published desc limit ?,?)");
293     query.bindValue(0, maxVideos);
294     query.bindValue(1, maxDeletions);
295     success = query.exec();
296     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
297 }