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

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 "artistmodel.h"
0006 
0007 #include <QStringBuilder>
0008 
0009 #include <asyncytmusic.h>
0010 
0011 ArtistModel::ArtistModel(QObject *parent)
0012     : AbstractYTMusicModel(parent)
0013     , m_view(albums, singles, songs, videos)
0014 {
0015     connect(this, &ArtistModel::channelIdChanged, this, [this] {
0016         if (m_channelId.isEmpty()) {
0017             return;
0018         }
0019 
0020         setLoading(true);
0021 
0022         auto future = YTMusicThread::instance()->fetchArtist(m_channelId);
0023         QCoro::connect(std::move(future), this, [=, this](artist::Artist &&artist) {
0024             setLoading(false);
0025 
0026             beginResetModel();
0027             m_artist = std::move(artist);
0028             std::sort(m_artist.thumbnails.begin(), m_artist.thumbnails.end());
0029 
0030             albums = m_artist.albums ? m_artist.albums->results : std::vector<artist::Artist::Album>();
0031             for (auto &album : albums) {
0032                 std::sort(album.thumbnails.begin(), album.thumbnails.end());
0033             }
0034             singles = m_artist.singles ? m_artist.singles->results : std::vector<artist::Artist::Single>();
0035             for (auto &single : singles) {
0036                 std::sort(single.thumbnails.begin(), single.thumbnails.end());
0037             }
0038             songs = m_artist.songs ? m_artist.songs->results : std::vector<artist::Artist::Song>();
0039             for (auto &song : songs) {
0040                 std::sort(song.thumbnails.begin(), song.thumbnails.end());
0041             }
0042             videos = m_artist.videos ? m_artist.videos->results : std::vector<artist::Artist::Video>();
0043             for (auto &video : videos) {
0044                 std::sort(video.thumbnails.begin(), video.thumbnails.end());
0045             }
0046 
0047             // std::span can't know if the data pointer underneath it was changed, so re-create
0048             m_view = MultiIterableView(albums, singles, songs, videos);
0049             endResetModel();
0050 
0051             Q_EMIT titleChanged();
0052             Q_EMIT thumbnailUrlChanged();
0053         });
0054     });
0055     connect(&YTMusicThread::instance().get(), &AsyncYTMusic::errorOccurred, this, [this] {
0056         setLoading(false);
0057     });
0058 }
0059 
0060 int ArtistModel::rowCount(const QModelIndex &parent) const
0061 {
0062     return parent.isValid() ? 0 : m_view.size();
0063 }
0064 
0065 QVariant ArtistModel::data(const QModelIndex &index, int role) const
0066 {
0067     switch (role) {
0068     case Title:
0069         return QString::fromStdString(std::visit([&](auto&& item) {
0070             using T = std::decay_t<decltype(item)>;
0071             if constexpr(std::is_same_v<T, artist::Artist::Album>) {
0072                 return item.title;
0073             } else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
0074                 return item.title;
0075             } else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
0076                 return item.title;
0077             } else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
0078                 return item.title;
0079             }
0080 
0081             Q_UNREACHABLE();
0082         }, m_view[index.row()]));
0083     case TypeRole:
0084         return std::visit([&](auto&& item) {
0085             using T = std::decay_t<decltype(item)>;
0086             if constexpr(std::is_same_v<T, artist::Artist::Album>) {
0087                 return Type::Album;
0088             } else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
0089                 return Type::Single;
0090             } else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
0091                 return Type::Song;
0092             } else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
0093                 return Type::Video;
0094             }
0095 
0096             Q_UNREACHABLE();
0097         }, m_view[index.row()]);
0098     case Artists:
0099         return QVariant::fromValue(std::vector<meta::Artist> {
0100             {
0101                 m_artist.name,
0102                 m_artist.channel_id
0103             }
0104         });
0105     case VideoId:
0106         return std::visit([&](auto&& item) {
0107             using T = std::decay_t<decltype(item)>;
0108             if constexpr(std::is_same_v<T, artist::Artist::Album>) {
0109                 return QVariant();
0110             } else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
0111                 return QVariant();
0112             } else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
0113                 return QVariant(QString::fromStdString(item.video_id));
0114             } else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
0115                 return QVariant(QString::fromStdString(item.video_id));
0116             }
0117 
0118             Q_UNREACHABLE();
0119         }, m_view[index.row()]);
0120     case ThumbnailUrl:
0121         return std::visit([&](auto&& item) {
0122             if (!item.thumbnails.empty()) {
0123                 return QString::fromStdString(item.thumbnails.front().url);
0124             }
0125             return QString();
0126         }, m_view[index.row()]);
0127     }
0128 
0129     Q_UNREACHABLE();
0130 
0131     return {};
0132 }
0133 
0134 QHash<int, QByteArray> ArtistModel::roleNames() const
0135 {
0136     return {
0137         {Title, "title"},
0138         {TypeRole, "type"},
0139         {Artists, "artists"},
0140         {VideoId, "videoId"},
0141         {ThumbnailUrl, "thumbnailUrl"}
0142     };
0143 }
0144 
0145 QString ArtistModel::channelId() const
0146 {
0147     return m_channelId;
0148 }
0149 
0150 void ArtistModel::setChannelId(const QString &channelId)
0151 {
0152     m_channelId = channelId;
0153     Q_EMIT channelIdChanged();
0154 }
0155 
0156 QString ArtistModel::title() const
0157 {
0158     return QString::fromStdString(m_artist.name);
0159 }
0160 
0161 QUrl ArtistModel::thumbnailUrl() const
0162 {
0163     if (m_artist.thumbnails.empty()) {
0164         return QUrl();
0165     }
0166 
0167     return QUrl(QString::fromStdString(m_artist.thumbnails.back().url));
0168 }
0169 
0170 QUrl ArtistModel::webUrl() const
0171 {
0172     return QUrl(YTMUSIC_WEB_BASE_URL % u"channel/" % m_channelId);
0173 }
0174 
0175 void ArtistModel::triggerItem(int row)
0176 {
0177     std::visit([&](auto&& item) {
0178         using T = std::decay_t<decltype(item)>;
0179         if constexpr(std::is_same_v<T, artist::Artist::Album>) {
0180             Q_EMIT openAlbum(QString::fromStdString(item.browse_id));
0181         } else if constexpr(std::is_same_v<T, artist::Artist::Single>) {
0182             Q_EMIT openAlbum(QString::fromStdString(item.browse_id));
0183         } else if constexpr(std::is_same_v<T, artist::Artist::Song>) {
0184             Q_EMIT openSong(QString::fromStdString(item.video_id));
0185         } else if constexpr(std::is_same_v<T, artist::Artist::Video>) {
0186             Q_EMIT openVideo(QString::fromStdString(item.video_id), QString::fromStdString(item.title));
0187         } else {
0188             Q_UNREACHABLE();
0189         }
0190     }, m_view[row]);
0191 }