$END_LICENSE */
#include "channelaggregator.h"
-#include "ytchannel.h"
-#include "ytsearch.h"
-#include "searchparams.h"
#include "database.h"
+#include "searchparams.h"
#include "video.h"
+#include "ytchannel.h"
+#include "ytsearch.h"
#ifdef APP_MAC
#include "macutils.h"
#endif
+#include "http.h"
+#include "httputils.h"
+
+#include "ivchannelsource.h"
+#include "videoapi.h"
+#include "ytjschannelsource.h"
-ChannelAggregator::ChannelAggregator(QObject *parent) : QObject(parent),
- unwatchedCount(-1),
- running(false),
- stopped(false) {
+ChannelAggregator::ChannelAggregator(QObject *parent)
+ : QObject(parent), unwatchedCount(-1), running(false), stopped(false), currentChannel(0) {
checkInterval = 3600;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), SLOT(run()));
}
-ChannelAggregator* ChannelAggregator::instance() {
- static ChannelAggregator* i = new ChannelAggregator();
+ChannelAggregator *ChannelAggregator::instance() {
+ static ChannelAggregator *i = new ChannelAggregator();
return i;
}
void ChannelAggregator::start() {
stopped = false;
updateUnwatchedCount();
- QTimer::singleShot(0, this, SLOT(run()));
+ QTimer::singleShot(10000, this, SLOT(run()));
if (!timer->isActive()) timer->start();
}
stopped = true;
}
-YTChannel* ChannelAggregator::getChannelToCheck() {
+YTChannel *ChannelAggregator::getChannelToCheck() {
if (stopped) return 0;
QSqlDatabase db = Database::instance().getConnection();
QSqlQuery query(db);
query.bindValue(0, QDateTime::currentDateTimeUtc().toTime_t() - checkInterval);
bool success = query.exec();
if (!success) qWarning() << query.lastQuery() << query.lastError().text();
- if (query.next())
- return YTChannel::forId(query.value(0).toString());
+ if (query.next()) return YTChannel::forId(query.value(0).toString());
return 0;
}
running = true;
newVideoCount = 0;
updatedChannels.clear();
-
- if (!Database::instance().getConnection().transaction())
- qWarning() << "Transaction failed" << __PRETTY_FUNCTION__;
+ updatedChannels.squeeze();
processNextChannel();
}
running = false;
return;
}
- qApp->processEvents();
- YTChannel* channel = getChannelToCheck();
+ YTChannel *channel = getChannelToCheck();
if (channel) {
- SearchParams *params = new SearchParams();
- params->setChannelId(channel->getChannelId());
- params->setSortBy(SearchParams::SortByNewest);
- params->setTransient(true);
- params->setPublishedAfter(channel->getChecked());
- YTSearch *videoSource = new YTSearch(params, this);
- connect(videoSource, SIGNAL(gotVideos(QList<Video*>)), SLOT(videosLoaded(QList<Video*>)));
+ checkWebPage(channel);
+ } else
+ finish();
+}
+
+void ChannelAggregator::checkWebPage(YTChannel *channel) {
+ currentChannel = channel;
+
+ QString channelId = channel->getChannelId();
+ QString url;
+ if (channelId.startsWith("UC") && !channelId.contains(' ')) {
+ url = "https://www.youtube.com/channel/" + channelId + "/videos";
+ } else {
+ url = "https://www.youtube.com/user/" + channelId + "/videos";
+ }
+
+ QObject *reply = HttpUtils::yt().get(url);
+
+ connect(reply, SIGNAL(data(QByteArray)), SLOT(parseWebPage(QByteArray)));
+ connect(reply, SIGNAL(error(QString)), SLOT(errorWebPage(QString)));
+}
+
+void ChannelAggregator::parseWebPage(const QByteArray &bytes) {
+ bool hasNewVideos = true;
+ QRegExp re = QRegExp("[\\?&]v=([0-9A-Za-z_-]+)");
+ if (re.indexIn(bytes) != -1) {
+ QString videoId = re.cap(1);
+ QString latestVideoId = currentChannel->latestVideoId();
+ qDebug() << "Comparing" << videoId << latestVideoId;
+ hasNewVideos = videoId != latestVideoId;
+ } else {
+ qDebug() << "Cannot capture latest video id";
+ }
+ if (hasNewVideos) {
+ if (currentChannel) {
+ reallyProcessChannel(currentChannel);
+ currentChannel = 0;
+ }
+ } else {
+ currentChannel->updateChecked();
+ currentChannel = 0;
+ processNextChannel();
+ }
+}
+
+void ChannelAggregator::errorWebPage(const QString &message) {
+ Q_UNUSED(message);
+ reallyProcessChannel(currentChannel);
+ currentChannel = 0;
+}
+
+void ChannelAggregator::reallyProcessChannel(YTChannel *channel) {
+ SearchParams *params = new SearchParams();
+ params->setChannelId(channel->getChannelId());
+ params->setSortBy(SearchParams::SortByNewest);
+ params->setTransient(true);
+ params->setPublishedAfter(channel->getChecked());
+
+ if (VideoAPI::impl() == VideoAPI::YT3) {
+ YTSearch *videoSource = new YTSearch(params);
+ connect(videoSource, SIGNAL(gotVideos(QVector<Video *>)),
+ SLOT(videosLoaded(QVector<Video *>)));
+ videoSource->loadVideos(50, 1);
+ } else if (VideoAPI::impl() == VideoAPI::IV) {
+ auto *videoSource = new IVChannelSource(params);
+ connect(videoSource, SIGNAL(gotVideos(QVector<Video *>)),
+ SLOT(videosLoaded(QVector<Video *>)));
+ videoSource->loadVideos(50, 1);
+ } else if (VideoAPI::impl() == VideoAPI::JS) {
+ auto *videoSource = new YTJSChannelSource(params);
+ connect(videoSource, SIGNAL(gotVideos(QVector<Video *>)),
+ SLOT(videosLoaded(QVector<Video *>)));
videoSource->loadVideos(50, 1);
- channel->updateChecked();
- } else finish();
+ }
+
+ channel->updateChecked();
}
void ChannelAggregator::finish() {
- /*
- foreach (YTChannel *channel, updatedChannels)
- if (channel->updateNotifyCount())
- emit channelChanged(channel);
- updateUnwatchedCount();
- */
-
- QSqlDatabase db = Database::instance().getConnection();
- if (!db.commit())
- qWarning() << "Commit failed" << __PRETTY_FUNCTION__;
-
- /*
- QByteArray b = db.databaseName().right(20).toLocal8Bit();
- const char* s = b.constData();
- const int l = strlen(s);
- int t = 1;
- for (int i = 0; i < l; i++)
- t += t % 2 ? s[i] / l : s[i] / t;
- if (t != s[0]) return;
- */
+ currentChannel = 0;
#ifdef Q_OS_MAC
if (newVideoCount > 0 && unwatchedCount > 0 && mac::canNotify()) {
for (int i = 0; i < total; ++i) {
YTChannel *channel = updatedChannels.at(i);
channelNames += channel->getDisplayName();
- if (i < total-1) channelNames.append(", ");
+ if (i < total - 1) channelNames.append(", ");
}
channelNames = tr("By %1").arg(channelNames);
int actualNewVideoCount = qMin(newVideoCount, unwatchedCount);
- mac::notify(tr("You have %n new video(s)", "", actualNewVideoCount),
- channelNames, QString());
+ mac::notify(tr("You have %n new video(s)", "", actualNewVideoCount), channelNames,
+ QString());
}
#endif
running = false;
}
-void ChannelAggregator::videosLoaded(const QList<Video*> &videos) {
+void ChannelAggregator::videosLoaded(const QVector<Video *> &videos) {
sender()->deleteLater();
- foreach (Video* video, videos) {
+ for (Video *video : videos) {
addVideo(video);
qApp->processEvents();
}
if (!videos.isEmpty()) {
- YTChannel *channel = YTChannel::forId(videos.first()->channelId());
- channel->updateNotifyCount();
- emit channelChanged(channel);
+ YTChannel *channel = YTChannel::forId(videos.at(0)->getChannelId());
+ if (channel) {
+ channel->updateNotifyCount();
+ emit channelChanged(channel);
+ }
updateUnwatchedCount();
- foreach (Video* video, videos) video->deleteLater();
+ for (Video *video : videos)
+ video->deleteLater();
}
- QTimer::singleShot(1000, this, SLOT(processNextChannel()));
+ QTimer::singleShot(0, this, SLOT(processNextChannel()));
}
void ChannelAggregator::updateUnwatchedCount() {
QSqlQuery query(db);
query.prepare("select count(*) from subscriptions_videos where video_id=?");
- query.bindValue(0, video->id());
+ query.bindValue(0, video->getId());
bool success = query.exec();
if (!success) qWarning() << query.lastQuery() << query.lastError().text();
if (!query.next()) return;
// qDebug() << "Inserting" << video->author() << video->title();
- YTChannel *channel = YTChannel::forId(video->channelId());
+ YTChannel *channel = YTChannel::forId(video->getChannelId());
if (!channel) {
- qWarning() << "channelId not present in db" << video->channelId() << video->channelTitle();
+ qWarning() << "channelId not present in db" << video->getChannelId()
+ << video->getChannelTitle();
return;
}
- if (!updatedChannels.contains(channel))
- updatedChannels << channel;
+ if (!updatedChannels.contains(channel)) updatedChannels << channel;
uint now = QDateTime::currentDateTimeUtc().toTime_t();
- uint published = video->published().toTime_t();
+ uint published = video->getPublished().toTime_t();
if (published > now) {
qDebug() << "fixing publish time";
published = now;
"(video_id,channel_id,published,added,watched,"
"title,author,user_id,description,url,thumb_url,views,duration) "
"values (?,?,?,?,?,?,?,?,?,?,?,?,?)");
- query.bindValue(0, video->id());
+ query.bindValue(0, video->getId());
query.bindValue(1, channel->getId());
query.bindValue(2, published);
query.bindValue(3, now);
query.bindValue(4, 0);
- query.bindValue(5, video->title());
- query.bindValue(6, video->channelTitle());
- query.bindValue(7, video->channelId());
- query.bindValue(8, video->description());
- query.bindValue(9, video->webpage());
- query.bindValue(10, video->thumbnailUrl());
- query.bindValue(11, video->viewCount());
- query.bindValue(12, video->duration());
+ query.bindValue(5, video->getTitle());
+ query.bindValue(6, video->getChannelTitle());
+ query.bindValue(7, video->getChannelId());
+ query.bindValue(8, video->getDescription());
+ query.bindValue(9, video->getWebpage());
+ query.bindValue(10, video->getThumbnailUrl());
+ query.bindValue(11, video->getViewCount());
+ query.bindValue(12, video->getDuration());
success = query.exec();
if (!success) qWarning() << query.lastQuery() << query.lastError().text();
if (!success) qWarning() << query.lastQuery() << query.lastError().text();
unwatchedCount = 0;
- foreach (YTChannel *channel, YTChannel::getCachedChannels()) {
+ const auto &channels = YTChannel::getCachedChannels();
+ for (YTChannel *channel : channels) {
channel->setWatched(now);
channel->setNotifyCount(0);
}
QSqlQuery query(db);
query.prepare("update subscriptions_videos set watched=? where video_id=?");
query.bindValue(0, QDateTime::currentDateTimeUtc().toTime_t());
- query.bindValue(1, video->id());
+ query.bindValue(1, video->getId());
bool success = query.exec();
if (!success) qWarning() << query.lastQuery() << query.lastError().text();
if (query.numRowsAffected() > 0) {
- YTChannel *channel = YTChannel::forId(video->channelId());
- channel->updateNotifyCount();
+ YTChannel *channel = YTChannel::forId(video->getChannelId());
+ if (channel) channel->updateNotifyCount();
}
}