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"