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

0001 // SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
0002 // SPDX-License-Identifier: GPL-3.0-or-later
0003 
0004 #include "searchmodel.h"
0005 
0006 #include "plasmatube.h"
0007 
0008 #include <KLocalizedString>
0009 
0010 #include <QFutureWatcher>
0011 #include <QNetworkReply>
0012 #include <QtConcurrent>
0013 
0014 SearchModel::SearchModel(QObject *parent)
0015     : AbstractListModel(parent)
0016 {
0017 }
0018 
0019 int SearchModel::rowCount(const QModelIndex &parent) const
0020 {
0021     if (parent.isValid())
0022         return 0;
0023     return m_results.size();
0024 }
0025 
0026 QVariant SearchModel::data(const QModelIndex &index, int role) const
0027 {
0028     if (!index.isValid() || index.parent().isValid())
0029         return {};
0030 
0031     const QInvidious::SearchResult &result = m_results.at(index.row());
0032     if (result.type() == QInvidious::SearchResult::Type::Video) {
0033         auto &video = result.video();
0034         switch (role) {
0035         case IdRole:
0036             return video.videoId();
0037         case TypeRole:
0038             return QStringLiteral("video");
0039         case TitleRole:
0040             return video.title();
0041         case ThumbnailRole: {
0042             const auto thumbnailUrl = video.thumbnail(QStringLiteral("medium")).url();
0043             if (thumbnailUrl.isRelative()) {
0044                 return QUrl(PlasmaTube::instance().sourceManager()->selectedSource()->api()->apiHost() + thumbnailUrl.toString(QUrl::FullyEncoded));
0045             }
0046             return thumbnailUrl;
0047         }
0048         case LengthRole:
0049             return video.length();
0050         case ViewCountRole:
0051             return video.viewCount();
0052         case AuthorRole:
0053             return video.author();
0054         case AuthorIdRole:
0055             return video.authorId();
0056         case AuthorUrlRole:
0057             return video.authorUrl();
0058         case PublishedRole:
0059             return video.published();
0060         case PublishedTextRole:
0061             return video.publishedText();
0062         case DescriptionRole:
0063             return video.description();
0064         case DescriptionHtmlRole:
0065             return video.descriptionHtml();
0066         case LiveNowRole:
0067             return video.liveNow();
0068         case PaidRole:
0069             return video.paid();
0070         case PremiumRole:
0071             return video.premium();
0072         case WatchedRole:
0073             return PlasmaTube::instance().selectedSource()->isVideoWatched(video.videoId());
0074         default:
0075             break;
0076         }
0077     } else if (result.type() == QInvidious::SearchResult::Type::Channel) {
0078         auto &channel = result.channel();
0079         switch (role) {
0080         case IdRole:
0081             return channel.id();
0082         case TypeRole:
0083             return QStringLiteral("channel");
0084         case ChannelNameRole:
0085             return channel.name();
0086         case ChannelAvatarRole:
0087             return channel.avatar();
0088         default:
0089             break;
0090         }
0091     } else if (result.type() == QInvidious::SearchResult::Type::Playlist) {
0092         auto &playlist = result.playlist();
0093         switch (role) {
0094         case IdRole:
0095             return playlist.id();
0096         case TypeRole:
0097             return QStringLiteral("playlist");
0098         case TitleRole:
0099             return playlist.title();
0100         case VideoCountRole:
0101             return playlist.videoCount();
0102         case ThumbnailRole:
0103             return playlist.thumbnail();
0104         default:
0105             break;
0106         }
0107     }
0108 
0109     return {};
0110 }
0111 
0112 void SearchModel::fetchMore(const QModelIndex &index)
0113 {
0114     if (canFetchMore(index)) {
0115         m_currentPage++;
0116         performSearch();
0117     }
0118 }
0119 
0120 bool SearchModel::canFetchMore(const QModelIndex &) const
0121 {
0122     return !m_futureWatcher;
0123 }
0124 
0125 void SearchModel::request(const SearchParameters *searchParameters)
0126 {
0127     if (!m_results.isEmpty()) {
0128         beginResetModel();
0129         m_results.clear();
0130         endResetModel();
0131     }
0132 
0133     m_currentPage = 1;
0134     m_searchParameters.fill(*searchParameters);
0135     performSearch();
0136 }
0137 
0138 void SearchModel::refresh()
0139 {
0140 }
0141 
0142 void SearchModel::markAsWatched(int index)
0143 {
0144     Q_UNUSED(index)
0145 }
0146 
0147 void SearchModel::markAsUnwatched(int index)
0148 {
0149     Q_UNUSED(index)
0150 }
0151 
0152 void SearchModel::removeFromPlaylist(const QString &plid, int index)
0153 {
0154     Q_UNUSED(plid)
0155     Q_UNUSED(index)
0156 }
0157 
0158 void SearchModel::performSearch()
0159 {
0160     auto selectedSource = PlasmaTube::instance().selectedSource();
0161     if (selectedSource == nullptr) {
0162         return;
0163     }
0164 
0165     m_searchParameters.setPage(m_currentPage);
0166 
0167     // stop running task
0168     if (m_futureWatcher) {
0169         // TODO: cancelling isn't implemented yet
0170         m_futureWatcher->cancel();
0171         m_futureWatcher->deleteLater();
0172         m_futureWatcher = nullptr;
0173     }
0174 
0175     // set up new task
0176     m_futureWatcher = new QFutureWatcher<QInvidious::SearchListResult>();
0177     connect(m_futureWatcher, &QFutureWatcherBase::finished, this, [this] {
0178         auto result = m_futureWatcher->result();
0179         if (auto videos = std::get_if<QList<QInvidious::SearchResult>>(&result)) {
0180             const auto rows = rowCount();
0181             beginInsertRows({}, rows, rows + videos->size() - 1);
0182             m_results << *videos;
0183             endInsertRows();
0184         } else if (auto error = std::get_if<QInvidious::Error>(&result)) {
0185             Q_EMIT errorOccured(error->second);
0186         }
0187 
0188         m_futureWatcher->deleteLater();
0189         m_futureWatcher = nullptr;
0190         setLoading(false);
0191     });
0192 
0193     m_futureWatcher->setFuture(selectedSource->api()->requestSearchResults(m_searchParameters));
0194     setLoading(true);
0195 }
0196 
0197 #include "moc_searchmodel.cpp"