File indexing completed on 2024-05-12 16:21:18

0001 // SPDX-FileCopyrightText: 2021 Jonah BrĂ¼chert <jbb@kaidan.im>
0002 //
0003 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 #include "searchmodel.h"
0006 #include "playlistutils.h"
0007 
0008 #include <ranges>
0009 
0010 SearchModel::SearchModel(QObject *parent)
0011     : AbstractYTMusicModel(parent)
0012 {
0013     connect(this, &SearchModel::searchQueryChanged, this, [this] {
0014         if (m_searchQuery.isEmpty()) {
0015             beginResetModel();
0016             m_searchResults.clear();
0017             endResetModel();
0018             return;
0019         }
0020 
0021         setLoading(true);
0022         auto future = YTMusicThread::instance()->search(m_searchQuery);
0023         QCoro::connect(std::move(future), this, [=, this](const std::vector<search::SearchResultItem> &&results) {
0024             beginResetModel();
0025             setLoading(false);
0026             m_searchResults = std::move(results);
0027 
0028             endResetModel();
0029         });
0030     });
0031     connect(&YTMusicThread::instance().get(), &AsyncYTMusic::errorOccurred, this, [this] {
0032         setLoading(false);
0033     });
0034 }
0035 
0036 int SearchModel::rowCount(const QModelIndex &parent) const
0037 {
0038     return parent.isValid() ? 0 : int(m_searchResults.size());
0039 }
0040 
0041 QVariant SearchModel::data(const QModelIndex &index, int role) const
0042 {
0043     switch (role) {
0044     case Title:
0045         return QString::fromStdString(std::visit([&](auto&& arg) {
0046             using T = std::decay_t<decltype(arg)>;
0047             if constexpr (std::is_same_v<T, search::Album>) {
0048                 return arg.title;
0049             } else if constexpr (std::is_same_v<T, search::Artist>) {
0050                 return arg.artist;
0051             } else if constexpr (std::is_same_v<T, search::Playlist>) {
0052                 return arg.title;
0053             } else if constexpr (std::is_same_v<T, search::Song>) {
0054                 return arg.title;
0055             } else if constexpr (std::is_same_v<T, search::Video>) {
0056                 return arg.title;
0057             } else if constexpr (std::is_same_v<T, search::TopResult>) {
0058                 if (arg.title) {
0059                     return *arg.title;
0060                 } else {
0061                     if (!arg.artists.empty()) {
0062                         return arg.artists.front().name;
0063                     } else {
0064                         return std::string();
0065                     }
0066                 }
0067             } else {
0068                 Q_UNREACHABLE();
0069             }
0070         }, m_searchResults.at(index.row())));
0071     case TypeRole:
0072         return std::visit([&](auto&& arg) {
0073             using T = std::decay_t<decltype(arg)>;
0074             if constexpr (std::is_same_v<T, search::Album>) {
0075                 return Type::Album;
0076             } else if constexpr (std::is_same_v<T, search::Artist>) {
0077                 return Type::Artist;
0078             } else if constexpr (std::is_same_v<T, search::Playlist>) {
0079                 return Type::Playlist;
0080             } else if constexpr (std::is_same_v<T, search::Song>) {
0081                 return Type::Song;
0082             } else if constexpr (std::is_same_v<T, search::Video>) {
0083                 return Type::Video;
0084             } else if constexpr (std::is_same_v<T, search::TopResult>) {
0085                 return Type::TopResult;
0086             } else {
0087                 Q_UNREACHABLE();
0088             }
0089         }, m_searchResults.at(index.row()));
0090     case VideoId:
0091         return std::visit([&](auto&& arg) {
0092             using T = std::decay_t<decltype(arg)>;
0093             if constexpr (std::is_same_v<T, search::Album>) {
0094                 return QString();
0095             } else if constexpr (std::is_same_v<T, search::Artist>) {
0096                 return QString();
0097             } else if constexpr (std::is_same_v<T, search::Playlist>) {
0098                 return QString();
0099             } else if constexpr (std::is_same_v<T, search::Song>) {
0100                 return QString::fromStdString(arg.video_id);
0101             } else if constexpr (std::is_same_v<T, search::Video>) {
0102                 return QString::fromStdString(arg.video_id);
0103             } else if constexpr (std::is_same_v<T, search::TopResult>) {
0104                 if (arg.video_id) {
0105                     return QString::fromStdString(*arg.video_id);
0106                 } else {
0107                     return QString();
0108                 }
0109             } else {
0110                 Q_UNREACHABLE();
0111             }
0112         }, m_searchResults.at(index.row()));
0113     case Artists:
0114         return QVariant::fromValue(std::visit([&](auto&& arg) {
0115             using T = std::decay_t<decltype(arg)>;
0116             if constexpr (std::is_same_v<T, search::Album>) {
0117                 return std::vector<meta::Artist>();
0118             } else if constexpr (std::is_same_v<T, search::Artist>) {
0119                 return std::vector<meta::Artist>();
0120             } else if constexpr (std::is_same_v<T, search::Playlist>) {
0121                 return std::vector<meta::Artist>();
0122             } else if constexpr (std::is_same_v<T, search::Song>) {
0123                 return arg.artists;
0124             } else if constexpr (std::is_same_v<T, search::Video>) {
0125                 return arg.artists;
0126             } else if constexpr (std::is_same_v<T, search::TopResult>) {
0127                 return arg.artists;
0128             } else {
0129                 Q_UNREACHABLE();
0130             }
0131         }, m_searchResults.at(index.row())));
0132     case RadioPlaylistId:
0133         return std::visit([&](auto &&arg) {
0134             using T = std::decay_t<decltype(arg)>;
0135 
0136             if constexpr (std::is_same_v<T, search::Artist>) {
0137                 if (arg.radio_id) {
0138                     return QString::fromStdString(*arg.radio_id);
0139                 }
0140             }
0141 
0142             return QString();
0143         }, m_searchResults.at(index.row()));
0144     case ThumbnailUrl:
0145         return std::visit([&](auto &&arg) {
0146             if (!arg.thumbnails.empty()) {
0147                 return QString::fromStdString(arg.thumbnails.front().url);
0148             }
0149 
0150             return QString();
0151         }, m_searchResults.at(index.row()));
0152     case ArtistsDisplayString:
0153         return QVariant::fromValue(std::visit([&](auto&& arg) {
0154             using T = std::decay_t<decltype(arg)>;
0155             if constexpr (std::is_same_v<T, search::Song> || std::is_same_v<T, search::Video> || std::is_same_v<T, search::Album>) {
0156                 return PlaylistUtils::artistsToString(arg.artists);
0157             } else if constexpr(std::is_same_v<T, search::TopResult>) {
0158                 if (arg.title) {
0159                     return PlaylistUtils::artistsToString(arg.artists);
0160                 }
0161             }
0162             return QString();
0163         }, m_searchResults.at(index.row())));
0164     }
0165 
0166     Q_UNREACHABLE();
0167 
0168     return {};
0169 }
0170 
0171 QHash<int, QByteArray> SearchModel::roleNames() const
0172 {
0173     return {
0174         {Title, "title"},
0175         {TypeRole, "type"},
0176         {VideoId, "videoId"},
0177         {Artists, "artists"},
0178         {ArtistsDisplayString, "artistsDisplayString"},
0179         {RadioPlaylistId, "radioPlaylistId"},
0180         {ThumbnailUrl, "thumbnailUrl"}
0181     };
0182 }
0183 
0184 QString SearchModel::searchQuery() const
0185 {
0186     return m_searchQuery;
0187 }
0188 
0189 void SearchModel::setSearchQuery(const QString &searchQuery)
0190 {
0191     m_searchQuery = searchQuery;
0192     Q_EMIT searchQueryChanged();
0193 }
0194 
0195 void SearchModel::triggerItem(int row)
0196 {
0197     std::visit([&](auto&& arg) {
0198         using T = std::decay_t<decltype(arg)>;
0199         if constexpr (std::is_same_v<T, search::Album>) {
0200             if (arg.browse_id) {
0201                 Q_EMIT openAlbum(QString::fromStdString(*arg.browse_id));
0202             }
0203         } else if constexpr (std::is_same_v<T, search::Artist>) {
0204             Q_EMIT openArtist(QString::fromStdString(arg.browse_id), QString::fromStdString(arg.radio_id.value_or("")), QString::fromStdString(arg.shuffle_id.value_or("")));
0205         } else if constexpr (std::is_same_v<T, search::Playlist>) {
0206             Q_EMIT openPlaylist(QString::fromStdString(arg.browse_id));
0207         } else if constexpr (std::is_same_v<T, search::Song>) {
0208             Q_EMIT openSong(QString::fromStdString(arg.video_id));
0209         } else if constexpr (std::is_same_v<T, search::Video>) {
0210             Q_EMIT openVideo(QString::fromStdString(arg.video_id), QString::fromStdString(arg.title));
0211         } else if constexpr (std::is_same_v<T, search::TopResult>) {
0212             if (arg.video_id) {
0213                 Q_EMIT openSong(QString::fromStdString(*arg.video_id));
0214             } else {
0215                 if (!arg.artists.empty() && arg.artists.front().id) {
0216                     Q_EMIT openArtist(QString::fromStdString(*arg.artists.front().id), {}, {});
0217                 }
0218             }
0219         } else {
0220             Q_UNREACHABLE();
0221         }
0222     }, m_searchResults.at(row));
0223 }
0224 
0225 
0226 int SearchModel::itemType(search::SearchResultItem const &item)
0227 {
0228     return std::visit([&](auto &&arg) {
0229         using T = std::decay_t<decltype(arg)>;
0230         if constexpr(std::is_same_v<T, search::Album>) {
0231             return SearchModel::Type::Album;
0232         } else if constexpr(std::is_same_v<T, search::Artist>) {
0233             return SearchModel::Type::Artist;
0234         } else if constexpr(std::is_same_v<T, search::Playlist>) {
0235             return SearchModel::Type::Playlist;
0236         } else if constexpr(std::is_same_v<T, search::Song>) {
0237             return SearchModel::Type::Song;
0238         } else if constexpr(std::is_same_v<T, search::Video>) {
0239             return SearchModel::Type::Video;
0240         } else if constexpr(std::is_same_v<T, search::TopResult>) {
0241             return SearchModel::Type::TopResult;
0242         } else {
0243             Q_UNREACHABLE();
0244         }
0245     }, item);
0246 }