3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
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.
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.
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/>.
21 #include "channelaggregator.h"
24 #include "searchparams.h"
31 ChannelAggregator::ChannelAggregator(QObject *parent) : QObject(parent),
36 checkInterval = settings.value("subscriptionsCheckInterval", 1800).toUInt();
38 timer = new QTimer(this);
39 timer->setInterval(60000 * 5);
40 connect(timer, SIGNAL(timeout()), SLOT(run()));
43 ChannelAggregator* ChannelAggregator::instance() {
44 static ChannelAggregator* i = new ChannelAggregator();
48 void ChannelAggregator::start() {
49 updateUnwatchedCount();
50 QTimer::singleShot(0, this, SLOT(run()));
54 void ChannelAggregator::stop() {
59 YTUser* ChannelAggregator::getChannelToCheck() {
60 if (stopped) return 0;
61 QSqlDatabase db = Database::instance().getConnection();
63 query.prepare("select user_id from subscriptions where checked<? "
64 "order by checked limit 1");
65 query.bindValue(0, QDateTime::currentDateTime().toTime_t() - checkInterval);
66 bool success = query.exec();
67 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
69 return YTUser::forId(query.value(0).toString());
73 void ChannelAggregator::run() {
76 if (!Database::exists()) return;
79 updatedChannels.clear();
80 if (!Database::instance().getConnection().transaction())
81 qWarning() << "Transaction failed" << __PRETTY_FUNCTION__;
85 void ChannelAggregator::processNextChannel() {
87 qApp->processEvents();
88 YTUser* user = getChannelToCheck();
90 SearchParams *params = new SearchParams();
91 params->setAuthor(user->getUserId());
92 params->setSortBy(SearchParams::SortByNewest);
93 params->setTransient(true);
94 YTSearch *videoSource = new YTSearch(params, this);
95 connect(videoSource, SIGNAL(gotVideos(QList<Video*>)),
96 SLOT(videosLoaded(QList<Video*>)));
97 videoSource->loadVideos(10, 1);
98 user->updateChecked();
102 void ChannelAggregator::finish() {
103 foreach (YTUser *user, updatedChannels)
104 if (user->updateNotifyCount())
105 emit channelChanged(user);
107 updateUnwatchedCount();
109 QSqlDatabase db = Database::instance().getConnection();
111 qWarning() << "Commit failed" << __PRETTY_FUNCTION__;
113 QByteArray b = db.databaseName().right(20).toLocal8Bit();
114 const char* s = b.constData();
115 const int l = strlen(s);
117 for (int i = 0; i < l; i++)
118 t += t % 2 ? s[i] / l : s[i] / t;
119 if (t != s[0]) return;
123 if (newVideoCount > 0 && unwatchedCount > 0 && mac::canNotify()) {
124 QString channelNames;
125 const int total = updatedChannels.size();
126 for (int i = 0; i < total; ++i) {
127 YTUser *user = updatedChannels.at(i);
128 channelNames += user->getDisplayName();
129 if (i < total-1) channelNames.append(", ");
131 channelNames = tr("By %1").arg(channelNames);
132 int actualNewVideoCount = qMin(newVideoCount, unwatchedCount);
133 mac::notify(tr("You have %n new video(s)", "", actualNewVideoCount),
134 channelNames, QString());
141 void ChannelAggregator::videosLoaded(QList<Video *> videos) {
142 sender()->deleteLater();
143 foreach (Video* video, videos) {
144 qApp->processEvents();
146 video->deleteLater();
148 processNextChannel();
151 void ChannelAggregator::updateUnwatchedCount() {
152 if (!Database::exists()) return;
153 QSqlDatabase db = Database::instance().getConnection();
155 query.prepare("select sum(notify_count) from subscriptions");
156 bool success = query.exec();
157 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
158 if (!query.next()) return;
159 int newUnwatchedCount = query.value(0).toInt();
160 if (newUnwatchedCount != unwatchedCount) {
161 unwatchedCount = newUnwatchedCount;
162 emit unwatchedCountChanged(unwatchedCount);
166 void ChannelAggregator::addVideo(Video *video) {
167 QSqlDatabase db = Database::instance().getConnection();
170 query.prepare("select count(*) from subscriptions_videos where video_id=?");
171 query.bindValue(0, video->id());
172 bool success = query.exec();
173 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
174 if (!query.next()) return;
175 int count = query.value(0).toInt();
176 if (count > 0) return;
178 // qDebug() << "Inserting" << video->author() << video->title();
180 QString userId = video->userId();
181 YTUser *user = YTUser::forId(userId);
182 if (!updatedChannels.contains(user))
183 updatedChannels << user;
184 int channelId = user->getId();
186 uint now = QDateTime::currentDateTime().toTime_t();
187 uint published = video->published().toTime_t();
188 if (published > now) {
189 qDebug() << "fixing publish time";
193 query = QSqlQuery(db);
194 query.prepare("insert into subscriptions_videos "
195 "(video_id,channel_id,published,added,watched,"
196 "title,author,user_id,description,url,thumb_url,views,duration) "
197 "values (?,?,?,?,?,?,?,?,?,?,?,?,?)");
198 query.bindValue(0, video->id());
199 query.bindValue(1, channelId);
200 query.bindValue(2, published);
201 query.bindValue(3, now);
202 query.bindValue(4, 0);
203 query.bindValue(5, video->title());
204 query.bindValue(6, video->author());
205 query.bindValue(7, video->userId());
206 query.bindValue(8, video->description());
207 query.bindValue(9, video->webpage());
208 query.bindValue(10, video->thumbnailUrl());
209 query.bindValue(11, video->viewCount());
210 query.bindValue(12, video->duration());
211 success = query.exec();
212 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
216 query = QSqlQuery(db);
217 query.prepare("update subscriptions set updated=? where user_id=?");
218 query.bindValue(0, published);
219 query.bindValue(1, userId);
220 success = query.exec();
221 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
224 void ChannelAggregator::markAllAsWatched() {
225 uint now = QDateTime::currentDateTime().toTime_t();
227 QSqlDatabase db = Database::instance().getConnection();
229 query.prepare("update subscriptions set watched=?, notify_count=0");
230 query.bindValue(0, now);
231 bool success = query.exec();
232 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
235 foreach (YTUser *user, YTUser::getCachedUsers()) {
236 user->setWatched(now);
237 user->setNotifyCount(0);
240 emit unwatchedCountChanged(0);
243 void ChannelAggregator::videoWatched(Video *video) {
244 if (!Database::exists()) return;
245 QSqlDatabase db = Database::instance().getConnection();
247 query.prepare("update subscriptions_videos set watched=? where video_id=?");
248 query.bindValue(0, QDateTime::currentDateTime().toTime_t());
249 query.bindValue(1, video->id());
250 bool success = query.exec();
251 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
252 if (query.numRowsAffected() > 0) {
253 YTUser *user = YTUser::forId(video->userId());
254 user->updateNotifyCount();
258 void ChannelAggregator::cleanup() {
259 static const int maxVideos = 1000;
260 static const int maxDeletions = 1000;
261 if (!Database::exists()) return;
262 QSqlDatabase db = Database::instance().getConnection();
265 bool success = query.exec("select count(*) from subscriptions_videos");
266 if (!success) qWarning() << query.lastQuery() << query.lastError().text();
267 if (!query.next()) return;
268 int count = query.value(0).toInt();
269 if (count <= maxVideos) return;
271 query = QSqlQuery(db);
272 query.prepare("delete from subscriptions_videos where id in "
273 "(select id from subscriptions_videos "
274 "order by published desc limit ?,?)");
275 query.bindValue(0, maxVideos);
276 query.bindValue(1, maxDeletions);
277 success = query.exec();
278 if (!success) qWarning() << query.lastQuery() << query.lastError().text();