File indexing completed on 2024-05-19 15:54:39

0001 // SPDX-FileCopyrightText: 2022 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 #pragma once
0006 
0007 #include <QObject>
0008 #include <QQuickAsyncImageProvider>
0009 #include <QNetworkAccessManager>
0010 #include <QAbstractListModel>
0011 #include <QSortFilterProxyModel>
0012 
0013 #include <ThreadedDatabase>
0014 
0015 #include <memory>
0016 
0017 #include "asyncytmusic.h"
0018 
0019 class FavouriteWatcher;
0020 class WasPlayedWatcher;
0021 
0022 struct Song {
0023     using ColumnTypes = std::tuple<QString, QString, QString, QString>;
0024 
0025     static Song fromSql(ColumnTypes tuple) {
0026         auto [videoId, title, artist, album] = tuple;
0027         return Song {videoId, title, artist, album};
0028     }
0029 
0030     QString videoId;
0031     QString title;
0032     QString artist;
0033     QString album;
0034 };
0035 
0036 class FavouritesModel : public QAbstractListModel {
0037     Q_OBJECT
0038 
0039     enum Roles {
0040         VideoId = Qt::UserRole + 1,
0041         Title,
0042         Artists,
0043         ArtistsDisplayString
0044     };
0045 
0046 public:
0047     FavouritesModel(QFuture<std::vector<Song>> &&songs, QObject *parent = nullptr);
0048 
0049     QHash<int, QByteArray> roleNames() const override;
0050     int rowCount(const QModelIndex &parent) const override;
0051     QVariant data(const QModelIndex &index, int role) const override;
0052     std::vector<Song> getFavouriteSongs() const;
0053 
0054 private:
0055     std::vector<Song> m_favouriteSongs;
0056 };
0057 
0058 struct PlayedSong {
0059     using ColumnTypes = std::tuple<QString, int, QString, QString, QString>;
0060 
0061     static PlayedSong fromSql(ColumnTypes tuple) {
0062         auto [videoId, plays, title, artist, album] = tuple;
0063         return PlayedSong {videoId, title, artist, album, plays};
0064     }
0065 
0066     QString videoId;
0067     QString title;
0068     QString artist;
0069     QString album;
0070     int plays;
0071 };
0072 
0073 class PlaybackHistoryModel : public QAbstractListModel {
0074     Q_OBJECT
0075 
0076 public:
0077     enum Roles {
0078         VideoId = Qt::UserRole + 1,
0079         Title,
0080         Artists,
0081         ArtistsDisplayString,
0082         Plays
0083     };
0084     Q_ENUM(Roles);
0085 
0086     PlaybackHistoryModel(QFuture<std::vector<PlayedSong>> &&songs, QObject *parent = nullptr);
0087     PlaybackHistoryModel(QObject *parent = nullptr);
0088 
0089     QHash<int, QByteArray> roleNames() const override;
0090     int rowCount(const QModelIndex &parent) const override;
0091     QVariant data(const QModelIndex &index, int role) const override;
0092     std::vector<PlayedSong> getPlayedSong() const;
0093 
0094 protected:
0095     std::vector<PlayedSong> m_playedSongs;
0096 };
0097 
0098 ///
0099 /// Provides a list of recently played songs matching a search query.
0100 ///
0101 class LocalSearchModel : public PlaybackHistoryModel {
0102     Q_OBJECT
0103 
0104     Q_PROPERTY(QString searchQuery MEMBER m_searchQuery NOTIFY searchQueryChanged)
0105 
0106     Q_SIGNAL void searchQueryChanged();
0107 
0108 public:
0109     LocalSearchModel(QObject *parent = nullptr);
0110 
0111     QString m_searchQuery;
0112 };
0113 
0114 class Library;
0115 
0116 class SearchHistoryModel : public QAbstractListModel {
0117     Q_OBJECT
0118 
0119     Q_PROPERTY(QString filter MEMBER m_filter NOTIFY filterChanged)
0120 
0121 public:
0122     SearchHistoryModel(Library *library);
0123     QHash<int, QByteArray> roleNames() const override;
0124     int rowCount(const QModelIndex &parent) const override;
0125     void removeSearch(const QString &search);
0126     QVariant data(const QModelIndex &index, int role) const override;
0127     void addSearch(QString const& search);
0128     const QString & temporarySearch() const;
0129     void setTemporarySearch(QString const& text);
0130 
0131     Q_SIGNAL void filterChanged();
0132 
0133 private:
0134     size_t getRow(QString const &search) const;
0135     std::vector<SingleValue<QString>> m_history;
0136     QString m_temporarySearch;
0137     QString m_filter;
0138 };
0139 
0140 class Library : public QObject
0141 {
0142     Q_OBJECT
0143     Q_PROPERTY(QAbstractListModel *favourites READ favourites NOTIFY favouritesChanged)
0144     Q_PROPERTY(QAbstractListModel *searches READ searches CONSTANT)
0145     Q_PROPERTY(QAbstractListModel *playbackHistory READ playbackHistory NOTIFY playbackHistoryChanged)
0146     Q_PROPERTY(QAbstractListModel *mostPlayed READ mostPlayed NOTIFY playbackHistoryChanged)
0147     Q_PROPERTY(QString temporarySearch READ temporarySearch WRITE setTemporarySearch NOTIFY temporarySearchChanged)
0148 
0149 public:
0150     explicit Library(QObject *parent = nullptr);
0151     ~Library();
0152 
0153     static Library &instance();
0154 
0155     FavouritesModel *favourites();
0156     Q_SIGNAL void favouritesChanged();
0157     void refreshFavourites();
0158     Q_INVOKABLE void addFavourite(const QString &videoId, const QString &title, const QString &artist, const QString &album);
0159     Q_INVOKABLE void removeFavourite(const QString &videoId);
0160     Q_INVOKABLE FavouriteWatcher *favouriteWatcher(const QString &videoId);
0161 
0162     SearchHistoryModel *searches();
0163     Q_SIGNAL void searchesChanged();
0164     Q_INVOKABLE void addSearch(const QString &text);
0165     Q_INVOKABLE void removeSearch(const QString &text);
0166     const QString& temporarySearch();
0167     void setTemporarySearch(const QString& text);
0168     Q_SIGNAL void temporarySearchChanged();
0169 
0170     PlaybackHistoryModel *playbackHistory();
0171     Q_SIGNAL void playbackHistoryChanged();
0172     void refreshPlaybackHistory();
0173     Q_INVOKABLE void addPlaybackHistoryItem(const QString &videoId, const QString &title, const QString &artist, const QString &album);
0174     Q_INVOKABLE void removePlaybackHistoryItem(const QString &videoId);
0175     Q_INVOKABLE WasPlayedWatcher *wasPlayedWatcher(const QString &videoId);
0176 
0177     Q_SIGNAL void playlistsChanged();
0178 
0179     PlaybackHistoryModel *mostPlayed();
0180 
0181     QNetworkAccessManager &nam();
0182     ThreadedDatabase &database() {
0183         return *m_database;
0184     }
0185     QFuture<void> addSong(const QString &videoId, const QString &title, const QString &artist, const QString &album);
0186 
0187 private:
0188 
0189     QNetworkAccessManager m_networkImageCacher;
0190     std::unique_ptr<ThreadedDatabase> m_database;
0191     SearchHistoryModel *m_searches;
0192     FavouritesModel *m_favourites;
0193     PlaybackHistoryModel *m_mostPlayed;
0194     PlaybackHistoryModel *m_playbackHistory;
0195 };
0196 
0197 class FavouriteWatcher : public QObject {
0198     Q_OBJECT
0199 
0200     Q_PROPERTY(bool isFavourite READ isFavourite NOTIFY isFavouriteChanged)
0201 
0202 public:
0203     FavouriteWatcher(Library *library, const QString &videoId);
0204 
0205     bool isFavourite() const;
0206     Q_SIGNAL void isFavouriteChanged();
0207 
0208     Q_SIGNAL void videoIdChanged();
0209 
0210 private:
0211     QString m_videoId;
0212     Library *m_library;
0213     bool m_isFavourite = false;
0214 };
0215 
0216 class WasPlayedWatcher : public QObject {
0217     Q_OBJECT
0218     
0219     Q_PROPERTY(bool wasPlayed READ wasPlayed NOTIFY wasPlayedChanged)
0220     
0221 public:
0222     WasPlayedWatcher(Library *Library, const QString &VideoId);
0223     
0224     bool wasPlayed() const;
0225     
0226     Q_SIGNAL void wasPlayedChanged();
0227 
0228 private:
0229     bool m_wasPlayed = false;
0230     QString m_videoId;
0231     Library *m_library;
0232     Q_SLOT void update(std::optional<SingleValue<bool> > result);
0233     Q_SLOT void query();
0234 };