File indexing completed on 2025-02-23 04:35:15

0001 // SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
0002 // SPDX-License-Identifier: GPL-3.0-or-later
0003 
0004 #include "videoqueue.h"
0005 
0006 #include "plasmatube.h"
0007 
0008 VideoQueue::VideoQueue(QObject *parent)
0009     : QAbstractListModel(parent)
0010 {
0011 }
0012 
0013 void VideoQueue::replace(const QStringList &videoIds)
0014 {
0015     beginResetModel();
0016     m_videoIds = videoIds;
0017     m_videoInfo.clear();
0018     m_videoInfo.resize(videoIds.size());
0019     endResetModel();
0020     Q_EMIT queueChanged();
0021 
0022     requestMissingVideoInformation();
0023 
0024     setCurrentIndex(0);
0025 }
0026 
0027 void VideoQueue::queueNext(const QString &videoId)
0028 {
0029     beginInsertRows({}, m_videoIds.size(), m_videoIds.size());
0030     m_videoIds.push_back(videoId);
0031     m_videoInfo.push_back(std::nullopt);
0032     endInsertRows();
0033     Q_EMIT queueChanged();
0034 
0035     requestMissingVideoInformation();
0036 }
0037 
0038 void VideoQueue::clear()
0039 {
0040     beginResetModel();
0041     m_videoIds.clear();
0042     m_videoInfo.clear();
0043     endResetModel();
0044     Q_EMIT queueChanged();
0045 
0046     setCurrentIndex(0);
0047 }
0048 
0049 void VideoQueue::playInQueue(const int videoIndex)
0050 {
0051     // TODO: protect against invalid indexes
0052     setCurrentIndex(videoIndex);
0053 }
0054 
0055 void VideoQueue::loadPlaylist(const QString &playlistId)
0056 {
0057     auto playlistFuture = PlasmaTube::instance().sourceManager()->selectedSource()->api()->requestPlaylist(playlistId);
0058 
0059     auto playlistFutureWatcher = new QFutureWatcher<QInvidious::VideoListResult>(this);
0060     connect(playlistFutureWatcher, &QFutureWatcherBase::finished, this, [this, playlistFutureWatcher] {
0061         auto result = playlistFutureWatcher->result();
0062 
0063         if (const auto videoList = std::get_if<QList<QInvidious::VideoBasicInfo>>(&result)) {
0064             QStringList videoIdList;
0065             std::transform(videoList->cbegin(), videoList->cend(), std::back_inserter(videoIdList), [](const auto &video) {
0066                 return video.videoId();
0067             });
0068 
0069             replace(videoIdList);
0070         } else if (const auto error = std::get_if<QInvidious::Error>(&result)) {
0071             qDebug() << "VideoQueue::loadPlaylist(): Error:" << error->second << error->first;
0072         }
0073 
0074         playlistFutureWatcher->deleteLater();
0075     });
0076     playlistFutureWatcher->setFuture(playlistFuture);
0077 }
0078 
0079 void VideoQueue::next()
0080 {
0081     if (canGoNext()) {
0082         setCurrentIndex(m_currentIndex + 1);
0083     }
0084 }
0085 
0086 bool VideoQueue::canGoNext() const
0087 {
0088     return m_currentIndex + 1 < m_videoIds.size();
0089 }
0090 
0091 void VideoQueue::previous()
0092 {
0093     if (canGoPrevious()) {
0094         m_currentIndex--;
0095         Q_EMIT currentVideoChanged();
0096     }
0097 }
0098 
0099 bool VideoQueue::canGoPrevious() const
0100 {
0101     return (m_currentIndex - 1) >= 0;
0102 }
0103 
0104 QString VideoQueue::getCurrentVideoId() const
0105 {
0106     if (m_videoIds.isEmpty()) {
0107         return {};
0108     }
0109     return m_videoIds[m_currentIndex];
0110 }
0111 
0112 int VideoQueue::rowCount(const QModelIndex &index) const
0113 {
0114     Q_UNUSED(index)
0115     return static_cast<int>(m_videoIds.size());
0116 }
0117 
0118 QVariant VideoQueue::data(const QModelIndex &index, int role) const
0119 {
0120     if (!checkIndex(index)) {
0121         return {};
0122     }
0123 
0124     const int row = index.row();
0125     if (role == IdRole) {
0126         return m_videoIds[row];
0127     } else if (role == PlayingRole) {
0128         return row == m_currentIndex;
0129     }
0130 
0131     if (m_videoInfo[row] != std::nullopt) {
0132         const auto &video = *m_videoInfo[row];
0133         switch (role) {
0134         case TitleRole:
0135             return video.title();
0136         case ThumbnailRole: {
0137             const auto thumbnailUrl = video.thumbnail(QStringLiteral("medium")).url();
0138             if (thumbnailUrl.isRelative()) {
0139                 return QUrl(PlasmaTube::instance().sourceManager()->selectedSource()->api()->apiHost() + thumbnailUrl.toString(QUrl::FullyEncoded));
0140             }
0141             return thumbnailUrl;
0142         }
0143         case LengthRole:
0144             return video.length();
0145         case ViewCountRole:
0146             return video.viewCount();
0147         case AuthorRole:
0148             return video.author();
0149         case AuthorIdRole:
0150             return video.authorId();
0151         case AuthorUrlRole:
0152             return video.authorUrl();
0153         case PublishedRole:
0154             return video.published();
0155         case PublishedTextRole:
0156             return video.publishedText();
0157         case DescriptionRole:
0158             return video.description();
0159         case DescriptionHtmlRole:
0160             return video.descriptionHtml();
0161         case LiveNowRole:
0162             return video.liveNow();
0163         case PaidRole:
0164             return video.paid();
0165         case PremiumRole:
0166             return video.premium();
0167         case WatchedRole:
0168             return PlasmaTube::instance().selectedSource()->isVideoWatched(video.videoId());
0169         default:
0170             break;
0171         }
0172     }
0173 
0174     return {};
0175 }
0176 
0177 QHash<int, QByteArray> VideoQueue::roleNames() const
0178 {
0179     return {{IdRole, "id"},
0180             {TitleRole, "title"},
0181             {ThumbnailRole, "thumbnail"},
0182             {LengthRole, "length"},
0183             {ViewCountRole, "viewCount"},
0184             {AuthorRole, "author"},
0185             {AuthorIdRole, "authorId"},
0186             {AuthorUrlRole, "authorUrl"},
0187             {PublishedRole, "published"},
0188             {PublishedTextRole, "publishedText"},
0189             {DescriptionRole, "description"},
0190             {DescriptionHtmlRole, "descriptionHtml"},
0191             {LiveNowRole, "liveNow"},
0192             {PaidRole, "paid"},
0193             {PremiumRole, "premium"},
0194             {WatchedRole, "watched"},
0195             {PlayingRole, "playing"}};
0196 }
0197 
0198 void VideoQueue::setCurrentIndex(const int newIndex)
0199 {
0200     const int oldIndex = m_currentIndex;
0201     m_currentIndex = newIndex;
0202     Q_EMIT currentVideoChanged();
0203 
0204     Q_EMIT dataChanged(index(oldIndex, 0), index(oldIndex, 0), {PlayingRole});
0205 
0206     Q_EMIT dataChanged(index(m_currentIndex, 0), index(m_currentIndex, 0), {PlayingRole});
0207 }
0208 
0209 void VideoQueue::requestMissingVideoInformation()
0210 {
0211     size_t i = 0;
0212     for (const auto &video : m_videoInfo) {
0213         // TODO: this doesn't cover the case where we could add another video while it's still fetchibng
0214         if (video == std::nullopt) {
0215             auto future = PlasmaTube::instance().sourceManager()->selectedSource()->api()->requestVideo(m_videoIds[i]);
0216 
0217             auto watcher = new QFutureWatcher<QInvidious::VideoResult>(this);
0218             connect(watcher, &QFutureWatcherBase::finished, this, [this, watcher, i] {
0219                 auto result = watcher->result();
0220 
0221                 if (const auto video = std::get_if<QInvidious::Video>(&result)) {
0222                     m_videoInfo[i] = *video;
0223                     Q_EMIT dataChanged(index(i, 0), index(i, 0));
0224                 } else if (const auto error = std::get_if<QInvidious::Error>(&result)) {
0225                     qDebug() << "VideoQueue::requestMissingVideoInformation(): Error:" << error->second << error->first;
0226                 }
0227 
0228                 watcher->deleteLater();
0229             });
0230             watcher->setFuture(future);
0231         }
0232         i++;
0233     }
0234 }
0235 
0236 bool VideoQueue::shouldBeVisible() const
0237 {
0238     return m_videoIds.size() > 1;
0239 }
0240 
0241 #include "moc_videoqueue.cpp"