File indexing completed on 2024-05-12 16:21:16
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 #pragma once 0006 0007 #include <QObject> 0008 #include <QThread> 0009 #include <QFuture> 0010 #include <QFutureWatcher> 0011 0012 #include <QCoroTask> 0013 #include <QCoroFuture> 0014 0015 #include <iostream> 0016 #include <vector> 0017 0018 #include <ytmusic.h> 0019 0020 constexpr QStringView YTMUSIC_WEB_BASE_URL = u"https://music.youtube.com/"; 0021 0022 Q_DECLARE_METATYPE(std::vector<artist::Artist::Album>); 0023 Q_DECLARE_METATYPE(std::vector<search::SearchResultItem>) 0024 Q_DECLARE_METATYPE(artist::Artist) 0025 Q_DECLARE_METATYPE(album::Album) 0026 Q_DECLARE_METATYPE(song::Song) 0027 Q_DECLARE_METATYPE(playlist::Playlist) 0028 Q_DECLARE_METATYPE(video_info::VideoInfo) 0029 Q_DECLARE_METATYPE(watch::Playlist) 0030 Q_DECLARE_METATYPE(std::optional<QString>) 0031 Q_DECLARE_METATYPE(std::vector<meta::Artist>) 0032 Q_DECLARE_METATYPE(meta::Artist) 0033 0034 /// 0035 /// Lazy initialized unique_ptr 0036 /// 0037 template <typename T> 0038 class Lazy { 0039 public: 0040 T *operator->() { 0041 return get().operator->(); 0042 } 0043 0044 inline std::unique_ptr<T> &get() { 0045 if (!m_item) { 0046 m_item = std::make_unique<T>(); 0047 } 0048 Q_ASSERT(m_item); 0049 return m_item; 0050 } 0051 0052 private: 0053 std::unique_ptr<T> m_item = nullptr; 0054 }; 0055 0056 class AsyncYTMusic : public QObject 0057 { 0058 friend class YTMusicThread; 0059 0060 Q_OBJECT 0061 0062 public: 0063 // public functions need to be thread safe 0064 QFuture<std::vector<search::SearchResultItem>> search(const QString &query); 0065 0066 QFuture<artist::Artist> fetchArtist(const QString &channelId); 0067 0068 QFuture<album::Album> fetchAlbum(const QString &browseId); 0069 0070 QFuture<std::optional<song::Song> > fetchSong(const QString &videoId); 0071 0072 QFuture<playlist::Playlist> fetchPlaylist(const QString &playlistId); 0073 0074 QFuture<std::vector<artist::Artist::Album>> fetchArtistAlbums(const QString &channelId, const QString ¶ms); 0075 0076 QFuture<video_info::VideoInfo> extractVideoInfo(const QString &videoId); 0077 0078 QFuture<watch::Playlist> fetchWatchPlaylist(const std::optional<QString> &videoId = std::nullopt , 0079 const std::optional<QString> &playlistId = std::nullopt); 0080 0081 QFuture<Lyrics> fetchLyrics(const QString &browseId); 0082 0083 QFuture<QString> version(); 0084 0085 Q_SIGNAL void errorOccurred(const QString &error); 0086 0087 protected: 0088 explicit AsyncYTMusic(QObject *parent = nullptr); 0089 0090 private: 0091 /// Invokes the given function on the thread of the YTMusic object, and handles exceptions that occur while invoking it. 0092 template <typename Func> 0093 QFuture<std::invoke_result_t<Func>> invokeAndCatchOnThread(Func fun) { 0094 using ReturnType = std::invoke_result_t<Func>; 0095 auto interface = std::make_shared<QFutureInterface<ReturnType>>(); 0096 QMetaObject::invokeMethod(this, [=, this]() { 0097 ReturnType val; 0098 try { 0099 val = fun(); 0100 } catch (const std::exception &err) { 0101 Q_EMIT errorOccurred(QString::fromLocal8Bit(err.what())); 0102 } 0103 0104 interface->reportResult(val); 0105 interface->reportFinished(); 0106 }); 0107 return interface->future(); 0108 } 0109 0110 // Python interpreter will be initialized from the thread calling the methods 0111 Lazy<YTMusic> m_ytm; 0112 }; 0113 0114 class YTMusicThread : private QThread { 0115 public: 0116 static YTMusicThread &instance(); 0117 ~YTMusicThread() override; 0118 0119 AsyncYTMusic *operator->(); 0120 AsyncYTMusic &get(); 0121 0122 private: 0123 YTMusicThread(); 0124 0125 AsyncYTMusic *m_ytm; 0126 };