]> git.sur5r.net Git - minitube/blobdiff - src/channelaggregator.cpp
New upstream version 3.8
[minitube] / src / channelaggregator.cpp
index a7eb0553449adb7e0dcd227362c379c346f267ec..d13779202f50cf714ca63689e96ec0c053931ab1 100644 (file)
@@ -19,19 +19,23 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $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);
@@ -39,15 +43,15 @@ ChannelAggregator::ChannelAggregator(QObject *parent) : QObject(parent),
     connect(timer, SIGNAL(timeout()), SLOT(run()));
 }
 
-ChannelAggregatorChannelAggregator::instance() {
-    static ChannelAggregatori = 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();
 }
 
@@ -56,7 +60,7 @@ void ChannelAggregator::stop() {
     stopped = true;
 }
 
-YTChannelChannelAggregator::getChannelToCheck() {
+YTChannel *ChannelAggregator::getChannelToCheck() {
     if (stopped) return 0;
     QSqlDatabase db = Database::instance().getConnection();
     QSqlQuery query(db);
@@ -65,8 +69,7 @@ YTChannel* ChannelAggregator::getChannelToCheck() {
     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;
 }
 
@@ -76,9 +79,7 @@ void ChannelAggregator::run() {
     running = true;
     newVideoCount = 0;
     updatedChannels.clear();
-
-    if (!Database::instance().getConnection().transaction())
-        qWarning() << "Transaction failed" << __PRETTY_FUNCTION__;
+    updatedChannels.squeeze();
 
     processNextChannel();
 }
@@ -88,42 +89,88 @@ void ChannelAggregator::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()) {
@@ -132,35 +179,38 @@ void ChannelAggregator::finish() {
         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() {
@@ -183,7 +233,7 @@ void ChannelAggregator::addVideo(Video *video) {
 
     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;
@@ -192,17 +242,17 @@ void ChannelAggregator::addVideo(Video *video) {
 
     // 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;
@@ -213,19 +263,19 @@ void ChannelAggregator::addVideo(Video *video) {
                   "(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();
 
@@ -250,7 +300,8 @@ void ChannelAggregator::markAllAsWatched() {
     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);
     }
@@ -264,12 +315,12 @@ void ChannelAggregator::videoWatched(Video *video) {
     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();
     }
 }