diff --git a/AudioPlayer.cpp b/AudioPlayer.cpp index c007d90..cdfa3ea 100644 --- a/AudioPlayer.cpp +++ b/AudioPlayer.cpp @@ -9,8 +9,11 @@ AudioPlayer::~AudioPlayer() delete player; } +AudioPlayer* AudioPlayer::instance = nullptr; + AudioPlayer::AudioPlayer(QObject *parent) : QObject(parent), player(new QMediaPlayer(this)), _currentTrack(nullptr) { + instance = this; player->setNotifyInterval(50); player->setVolume(50); _volume = 0.5; diff --git a/AudioPlayer.hpp b/AudioPlayer.hpp index 46d7665..5533d78 100644 --- a/AudioPlayer.hpp +++ b/AudioPlayer.hpp @@ -41,6 +41,8 @@ class AudioPlayer : public QObject inline static refTrack noneTrack{new Track()}; + static AudioPlayer* instance; + public slots: void play(refTrack track); void play(refPlaylist playlist); diff --git a/ID.cpp b/ID.cpp index 1cbf573..195c0d8 100644 --- a/ID.cpp +++ b/ID.cpp @@ -71,7 +71,7 @@ refPlaylist ID::toPlaylist() const else if (kind == dkPlaylist) { if (client == ckYandex) { if (YClient::instance == nullptr) return nullptr; - if (id == -1) return refPlaylist(YClient::instance->userDailyPlaylist()); + if (id == -1) return YClient::instance->userDailyPlaylist()->toPlaylist(); return refPlaylist(YClient::instance->playlist(id)); } if (client == ckNone) { diff --git a/PlaylistEntry.qml b/PlaylistEntry.qml index 4abebe8..82f8b43 100644 --- a/PlaylistEntry.qml +++ b/PlaylistEntry.qml @@ -11,7 +11,7 @@ Item { property real _anim_n: 0 property real _anim2_n: 0 -// property QmlPlaylist playlist + property YPlaylist playlist signal play() signal pause() @@ -56,8 +56,7 @@ Item { height: root.width sourceSize: Qt.size(root.width, root.width) -// source: playlist.cover - source: "qrc:/resources/player/no-cover.svg" + source: playlist == null? "qrc:/resources/player/no-cover.svg" : playlist.cover fillMode: Image.PreserveAspectCrop Rectangle { @@ -133,8 +132,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap -// text: playlist.name - text: "some playlist" // " " + text: playlist == null? qsTr("some playlist") : playlist.name MouseArea { id: _textMouse diff --git a/main.cpp b/main.cpp index fa899f6..d5fb3db 100644 --- a/main.cpp +++ b/main.cpp @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); app.installTranslator(&translator); - bool gui = args.count() == 0 || args.has("-g") || args.has("--gui"); +// bool gui = args.count() == 0 || args.has("-g") || args.has("--gui"); if (args.has("-v") || args.has("--version")) { std::cout << "DMusic 0.1" << std::endl; @@ -65,6 +65,7 @@ int main(int argc, char *argv[]) qmlRegisterType("DMusic", 1, 0, "RemoteMediaController"); qmlRegisterType("DMusic", 1, 0, "Clipboard"); qmlRegisterType("DMusic", 1, 0, "DFileDialog"); + qmlRegisterType("DMusic", 1, 0, "YPlaylist"); qmlRegisterSingletonType("DMusic", 1, 0, "Messages", &Messages::qmlInstance); qmlRegisterSingletonType("DMusic", 1, 0, "YClient", &YClient::qmlInstance); diff --git a/main.qml b/main.qml index 6f93c2d..ec294f4 100644 --- a/main.qml +++ b/main.qml @@ -52,12 +52,17 @@ Window { } } + function afterLogin() { + _userDailyPlaylist.playlist = YClient.userDailyPlaylist() + _userLikedPlaylist.playlist = YClient.userLikedTracksPlaylist() + } + function autologin() { if (_settings.ym_token == "") return if (_settings.ym_proxyServer == "") { - YClient.login(_settings.ym_token, function(_){}) + YClient.login(_settings.ym_token, afterLogin) } else { - YClient.loginViaProxy(_settings.ym_token, _settings.ym_proxyServer, function(_){}) + YClient.loginViaProxy(_settings.ym_token, _settings.ym_proxyServer, afterLogin) } } @@ -197,9 +202,9 @@ Window { if (!available()) _openMedia.open() else { let media = sellect(qsTr("Chose media"), "*.mp3 *.wav *.ogg *.m4a", qsTr("Audio (*.mp3 *.wav *.ogg *.m4a)")) - if (media == "") return + if (media === "") return let cover = sellect(qsTr("Chose cover"), "*.jpg *.png *.svg", qsTr("Image (*.jpg *.png *.svg)")) - if (cover == "") { + if (cover === "") { YClient.addUserTrack(media, "", _tb_title.text, _tb_artists.text, _tb_extra.text) } else { YClient.addUserTrack(media, cover, _tb_title.text, _tb_artists.text, _tb_extra.text) @@ -237,16 +242,18 @@ Window { anchors.leftMargin: 25 anchors.topMargin: 25 - onPlay: _player.player.play(YClient.playlist(3)) +// onPlay: _player.player.play(YClient.playlist(3)) + onPlay: YClient.playPlaylist(playlist) } PlaylistEntry { + id: _userDailyPlaylist anchors.left: _userLikedPlaylist.right anchors.top: _title.bottom anchors.leftMargin: 25 anchors.topMargin: 25 - onPlay: _player.player.play(YClient.userDailyPlaylist()) + onPlay: YClient.playPlaylist(playlist) } ListModel { diff --git a/python.hpp b/python.hpp index 32d3ece..b9a239d 100644 --- a/python.hpp +++ b/python.hpp @@ -81,7 +81,7 @@ namespace py Iterator begin() const; EndIterator end() const; - PyObject* raw; + PyObject* raw = nullptr; }; struct error : std::exception diff --git a/yapi.cpp b/yapi.cpp index f2671ba..46d127d 100644 --- a/yapi.cpp +++ b/yapi.cpp @@ -10,6 +10,7 @@ #include "settings.hpp" #include "utils.hpp" #include "Messages.hpp" +#include "AudioPlayer.hpp" using namespace py; @@ -663,6 +664,17 @@ Playlist* YClient::likedTracks() return res; } +YPlaylist* YClient::userLikedTracksPlaylist() +{ + if (!initialized()) return nullptr; + try { + return new YPlaylist(me.call("playlists_list", me.get("me").get("account").get("uid").to() + ":" + QString::number(3))[0]); + } catch (py::error& e) { + Messages::error(tr("Failed to load Yandex.Music user liked tracks (playlist with id 3)"), e.what()); + } + return nullptr; +} + Playlist* YClient::playlist(int id) { if (id == 3) return likedTracks(); @@ -689,22 +701,16 @@ Playlist* YClient::oneTrack(qint64 id) return res; } -Playlist* YClient::userDailyPlaylist() +YPlaylist* YClient::userDailyPlaylist() { - DPlaylist* res = new DPlaylist(this); - if (!initialized()) return res; + if (!initialized()) return nullptr; try { auto ppb = me.call("landing", std::vector{"personalplaylists"}).get("blocks")[0]; - auto daily = ppb.get("entities")[0].get("data").get("data"); - auto a = daily.call("fetch_tracks"); - for (auto&& p : a) { - if (!p.has("id")) continue; - res->add(track(p.get("id").to())); - } + return new YPlaylist(ppb.get("entities")[0].get("data").get("data")); } catch (py::error& e) { Messages::error(tr("Failed to load Yandex.Music daily playlist"), e.what()); } - return res; + return nullptr; } Playlist* YClient::userTrack(int id) @@ -727,7 +733,7 @@ Playlist* YClient::downloadsPlaylist() } recoredDir = QDir("user"); allFiles = recoredDir.entryList(QDir::Files, QDir::SortFlag::Name); - for (auto s : allFiles) { + for (auto s : qAsConst(allFiles)) { if (!s.endsWith(".json")) continue; s.chop(5); res->add(refTrack(new UserTrack(s.toInt()))); @@ -735,7 +741,63 @@ Playlist* YClient::downloadsPlaylist() return res; } +void YClient::playPlaylist(YPlaylist* playlist) +{ + if (playlist == nullptr) return; + AudioPlayer::instance->play(playlist->toPlaylist()); +} + void YClient::addUserTrack(QString media, QString cover, QString title, QString artists, QString extra) { UserTrack().setup(media, cover, title, artists, extra); } + +YPlaylist::YPlaylist(py::object impl, QObject* parent) : QObject(parent), impl(impl) +{ + +} + +YPlaylist::YPlaylist() +{ + +} + +QString YPlaylist::name() +{ + return impl.get("title").to(); +} + +QUrl YPlaylist::cover() +{ + try { + auto a = "http://" + impl.get("cover").get("uri").to(); + return QUrl(a.replace("%%", "m" + Settings::ym_coverQuality())); + } catch (py::error& e) { + return QUrl("qrc:resources/player/no-cover.svg"); + } +} + +refPlaylist YPlaylist::toPlaylist() +{ + DPlaylist* res = new DPlaylist(this); + auto a = impl.call("fetch_tracks"); + for (auto&& p : a) { + if (!p.has("id")) continue; + res->add(refTrack(new YTrack(p.get("id").to(), YClient::instance))); + } + return refPlaylist(res); +} + +bool YPlaylist::setName(QString name) +{ + Q_UNUSED(name) + // TODO + return false; +} + +bool YPlaylist::setCover(QUrl cover) +{ + Q_UNUSED(cover) + // TODO + return false; +} diff --git a/yapi.hpp b/yapi.hpp index c6597c3..a26c71b 100644 --- a/yapi.hpp +++ b/yapi.hpp @@ -106,12 +106,28 @@ struct YPlaylist : QObject public: YPlaylist(py::object impl, QObject* parent = nullptr); YPlaylist(); - PyObject* raw() { return impl.raw; } + PyObject* raw() const { return impl.raw; } + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QUrl cover READ cover WRITE setCover NOTIFY coverChanged) + + QString name(); + QUrl cover(); + refPlaylist toPlaylist(); + +public slots: + bool setName(QString name); + bool setCover(QUrl cover); + +signals: + void nameChanged(QString name); + void coverChanged(QUrl cover); private: py::object impl; - int _id; }; +inline PyObject* toPyObject(YPlaylist const& a) { Py_INCREF(a.raw()); return a.raw(); } +inline void fromPyObject(py::object const& o, YPlaylist*& res) { res = new YPlaylist(o.raw); } struct YClient : QObject { @@ -143,12 +159,15 @@ public slots: QVector fetchTracks(qint64 id); Playlist* likedTracks(); + YPlaylist* userLikedTracksPlaylist(); Playlist* playlist(int id); Playlist* oneTrack(qint64 id); - Playlist* userDailyPlaylist(); + YPlaylist* userDailyPlaylist(); Playlist* userTrack(int id); Playlist* downloadsPlaylist(); + void playPlaylist(YPlaylist* playlist); + void addUserTrack(QString media, QString cover, QString title, QString artists, QString extra); signals: