From 2e67805e9191a870f6b1ff7b76adae4e4a90b28b Mon Sep 17 00:00:00 2001 From: Eism Date: Tue, 3 Dec 2024 18:24:54 +0200 Subject: [PATCH] updated the API --- src/framework/global/serialization/json.cpp | 5 ++ src/framework/global/serialization/json.h | 2 + src/musesounds/imusesoundsconfiguration.h | 3 + .../internal/musesoundsconfiguration.cpp | 9 +- .../internal/musesoundsconfiguration.h | 1 + .../internal/musesoundsrepository.cpp | 89 ++++++++++++------- .../internal/musesoundsrepository.h | 2 +- src/musesounds/musesoundstypes.h | 6 +- .../MuseScore/MuseSounds/MuseSoundsPage.qml | 8 +- .../MuseSounds/internal/MuseSoundItem.qml | 13 --- .../internal/MuseSoundsListView.qml | 18 ++-- src/musesounds/view/musesoundslistmodel.cpp | 15 +++- src/musesounds/view/musesoundslistmodel.h | 3 + 13 files changed, 111 insertions(+), 63 deletions(-) diff --git a/src/framework/global/serialization/json.cpp b/src/framework/global/serialization/json.cpp index b34d12eb0d4ae..c00bbb615abd7 100644 --- a/src/framework/global/serialization/json.cpp +++ b/src/framework/global/serialization/json.cpp @@ -412,6 +412,11 @@ void JsonArray::resize(size_t i) array_mut(m_data).resize(i); } +bool JsonArray::empty() const +{ + return array_const(m_data).size() == 0; +} + JsonValue JsonArray::at(size_t i) const { std::shared_ptr d = std::make_shared(); diff --git a/src/framework/global/serialization/json.h b/src/framework/global/serialization/json.h index 929f43367ecf6..1df2d0f0d32e9 100644 --- a/src/framework/global/serialization/json.h +++ b/src/framework/global/serialization/json.h @@ -109,6 +109,8 @@ class JsonArray size_t size() const; void resize(size_t i); + bool empty() const; + JsonValue at(size_t i) const; JsonArray& set(size_t i, bool v); diff --git a/src/musesounds/imusesoundsconfiguration.h b/src/musesounds/imusesoundsconfiguration.h index 5411705ee250e..d8a4ef6ef6df2 100644 --- a/src/musesounds/imusesoundsconfiguration.h +++ b/src/musesounds/imusesoundsconfiguration.h @@ -21,6 +21,8 @@ */ #pragma once +#include "types/string.h" + #include "modularity/imoduleinterface.h" #include "network/networktypes.h" @@ -36,5 +38,6 @@ class IMuseSoundsConfiguration : MODULE_EXPORT_INTERFACE virtual muse::network::RequestHeaders headers() const = 0; virtual QUrl soundsUrl() const = 0; + virtual QUrl soundPageUrl(const muse::String& soundCode) const = 0; }; } diff --git a/src/musesounds/internal/musesoundsconfiguration.cpp b/src/musesounds/internal/musesoundsconfiguration.cpp index 2eed98c5633d5..fee16b8dde47a 100644 --- a/src/musesounds/internal/musesoundsconfiguration.cpp +++ b/src/musesounds/internal/musesoundsconfiguration.cpp @@ -30,6 +30,8 @@ using namespace muse::network; static const std::string module_name("musesounds"); static const Settings::Key GET_SOUNDS_TESTING_MODE_KEY(module_name, "musesounds/getSoundsTestingMode"); +static const muse::String OPEN_SOUND_URL("https://www.musehub.com/muse-sounds/"); + void MuseSoundsConfiguration::init() { settings()->setDefaultValue(GET_SOUNDS_TESTING_MODE_KEY, Val(false)); @@ -38,7 +40,7 @@ void MuseSoundsConfiguration::init() RequestHeaders MuseSoundsConfiguration::headers() const { RequestHeaders headers; - headers.rawHeaders["Accept"] = "application/json"; + headers.rawHeaders["Content-Type"] = "application/json"; return headers; } @@ -48,6 +50,11 @@ QUrl MuseSoundsConfiguration::soundsUrl() const : QUrl("https://cosmos-customer-webservice-dev.azurewebsites.net/graphql"); } +QUrl MuseSoundsConfiguration::soundPageUrl(const muse::String& soundCode) const +{ + return QUrl(OPEN_SOUND_URL + soundCode); +} + bool MuseSoundsConfiguration::isTestingMode() const { return settings()->value(GET_SOUNDS_TESTING_MODE_KEY).toBool(); diff --git a/src/musesounds/internal/musesoundsconfiguration.h b/src/musesounds/internal/musesoundsconfiguration.h index f239363033621..52fe8efa83819 100644 --- a/src/musesounds/internal/musesoundsconfiguration.h +++ b/src/musesounds/internal/musesoundsconfiguration.h @@ -40,6 +40,7 @@ class MuseSoundsConfiguration : public IMuseSoundsConfiguration, public muse::In muse::network::RequestHeaders headers() const override; QUrl soundsUrl() const override; + QUrl soundPageUrl(const muse::String& soundCode) const override; private: bool isTestingMode() const; diff --git a/src/musesounds/internal/musesoundsrepository.cpp b/src/musesounds/internal/musesoundsrepository.cpp index 5d1e920c342c9..dd2f1bc0768b1 100644 --- a/src/musesounds/internal/musesoundsrepository.cpp +++ b/src/musesounds/internal/musesoundsrepository.cpp @@ -30,6 +30,13 @@ using namespace mu::musesounds; using namespace muse; using namespace muse::network; +static muse::Uri correctThumbnailSize(const Uri& uri) +{ + String uriStr = String::fromStdString(uri.toString()); + uriStr.replace(u"/library-images/", u"/cdn-cgi/image/w=240,q=80,f=webp/library-images/"); + return Uri(uriStr.toStdString()); +} + void MuseSoundsRepository::init() { auto soundsCallBack = [this](const RetVal& result) { @@ -46,7 +53,7 @@ void MuseSoundsRepository::init() m_soundsCategoriesChanged.notify(); }; - Concurrent::run(this, &MuseSoundsRepository::th_requestSounds, soundsRequestUrl(), soundsCallBack); + Concurrent::run(this, &MuseSoundsRepository::th_requestSounds, configuration()->soundsUrl(), soundsCallBack); } const MuseSoundCategoryInfoList& MuseSoundsRepository::soundsCategoryList() const @@ -60,38 +67,37 @@ async::Notification MuseSoundsRepository::soundsCategoryListChanged() const return m_soundsCategoriesChanged; } -QUrl MuseSoundsRepository::soundsRequestUrl() const +QByteArray MuseSoundsRepository::soundsRequestJson() const { String locale = QLocale().name(); String query = String( - R"( - query MyQuery { - product_pages_configuration(version: "default") { + R"(query MyQuery { + product_pages_configuration { librariesPageSections { ... on ProductPageSectionRegular { title(locale: {locale: "%1"}) productCards { ... on ProductCardRegular { - title(locale: {locale: "%1"}) - coverImageUrl - description(locale: {locale: "%1"}) + iconImageUrl + product { + ... on ProductLibrary { + title(locale: {locale: "%1"}) + subtitle(locale: {locale: "%1"}) + code + } + } } } } } } - } - )").arg(locale); + })").arg(locale); - StringList params = { - "query=" + query - }; - - QUrl url = configuration()->soundsUrl(); - url.setQuery(params.join(u"&")); + JsonObject json; + json["query"] = query; - return url; + return JsonDocument(json).toJson(JsonDocument::Format::Compact).toQByteArray(); } void MuseSoundsRepository::th_requestSounds(const QUrl& soundsUrl, std::function)> callBack) const @@ -101,14 +107,17 @@ void MuseSoundsRepository::th_requestSounds(const QUrl& soundsUrl, std::function network::INetworkManagerPtr networkManager = networkManagerCreator()->makeNetworkManager(); RequestHeaders headers = configuration()->headers(); - QBuffer soundsInfoData; - Ret soundsItemsRet = networkManager->get(soundsUrl, &soundsInfoData, headers); + QByteArray jsonData = soundsRequestJson(); + QBuffer receivedData(&jsonData); + OutgoingDevice device(&receivedData); + + Ret soundsItemsRet = networkManager->post(soundsUrl, &device, &receivedData, headers); if (!soundsItemsRet) { callBack(soundsItemsRet); return; } - JsonDocument soundsInfoDoc = JsonDocument::fromJson(ByteArray::fromQByteArray(soundsInfoData.data())); + JsonDocument soundsInfoDoc = JsonDocument::fromJson(ByteArray::fromQByteArray(receivedData.data())); RetVal result; result.ret = make_ret(Ret::Code::Ok); @@ -123,28 +132,48 @@ MuseSoundCategoryInfoList MuseSoundsRepository::parseSounds(const JsonDocument& JsonObject obj = soundsDoc.rootObject(); JsonObject data = !obj.empty() ? obj.value("data").toObject() : JsonObject(); - JsonObject productsSearch = !data.empty() ? data.value("products_search").toObject() : JsonObject(); - JsonArray items = !productsSearch.empty() ? productsSearch.value("items").toArray() : JsonArray(); + JsonObject productsSearch = !data.empty() ? data.value("product_pages_configuration").toObject() : JsonObject(); + JsonArray categories = !productsSearch.empty() ? productsSearch.value("librariesPageSections").toArray() : JsonArray(); - for (size_t i = 0; i < items.size(); i++) { - JsonObject itemObj = items.at(i).toObject(); + for (size_t i = 0; i < categories.size(); ++i) { + JsonObject categoryObj = categories.at(i).toObject(); + if (categoryObj.empty()) { + continue; + } MuseSoundCategoryInfo category; - category.title = itemObj.value("title").toString(); + category.title = categoryObj.value("title").toString(); - JsonArray soundsItems = itemObj.value("items").toArray(); + JsonArray soundsItems = categoryObj.value("productCards").toArray(); + if (soundsItems.empty()) { + continue; + } - for (size_t i = 0; i < soundsItems.size(); i++) { + for (size_t i = 0; i < soundsItems.size(); ++i) { JsonObject soundItemObj = soundsItems.at(i).toObject(); + if (soundItemObj.empty()) { + continue; + } + + JsonObject productObj = soundItemObj.value("product").toObject(); + if (productObj.empty()) { + continue; + } MuseSoundInfo sound; - sound.title = itemObj.value("title").toString(); - sound.description = itemObj.value("description").toString(); - sound.thumbnail = itemObj.value("coverImageUrl").toString(); + sound.title = productObj.value("title").toString(); + sound.description = productObj.value("subtitle").toString(); + sound.thumbnail = correctThumbnailSize(Uri(soundItemObj.value("iconImageUrl").toStdString())); + sound.code = productObj.value("code").toString(); + sound.uri = Uri(configuration()->soundPageUrl(sound.code).toString().toStdString()); category.sounds.emplace_back(sound); } + if (category.sounds.empty()) { + continue; + } + result.emplace_back(category); } diff --git a/src/musesounds/internal/musesoundsrepository.h b/src/musesounds/internal/musesoundsrepository.h index b8366b4bd7b71..f7be486b3a342 100644 --- a/src/musesounds/internal/musesoundsrepository.h +++ b/src/musesounds/internal/musesoundsrepository.h @@ -48,7 +48,7 @@ class MuseSoundsRepository : public IMuseSoundsRepository, public muse::Injectab muse::async::Notification soundsCategoryListChanged() const override; private: - QUrl soundsRequestUrl() const; + QByteArray soundsRequestJson() const; void th_requestSounds(const QUrl& soundsUrl, std::function)> callBack) const; MuseSoundCategoryInfoList parseSounds(const muse::JsonDocument& soundsDoc) const; diff --git a/src/musesounds/musesoundstypes.h b/src/musesounds/musesoundstypes.h index 126567b816b56..a2c4017c2b988 100644 --- a/src/musesounds/musesoundstypes.h +++ b/src/musesounds/musesoundstypes.h @@ -22,13 +22,15 @@ #pragma once #include "types/string.h" -#include "io/path.h" +#include "types/uri.h" namespace mu::musesounds { struct MuseSoundInfo { + muse::String code; muse::String title; muse::String description; - muse::io::path_t thumbnail; + muse::Uri thumbnail; + muse::Uri uri; }; using MuseSoundInfoList = std::vector; diff --git a/src/musesounds/qml/MuseScore/MuseSounds/MuseSoundsPage.qml b/src/musesounds/qml/MuseScore/MuseSounds/MuseSoundsPage.qml index 6940b3ca08b6c..7444b6dee66bc 100644 --- a/src/musesounds/qml/MuseScore/MuseSounds/MuseSoundsPage.qml +++ b/src/musesounds/qml/MuseScore/MuseSounds/MuseSoundsPage.qml @@ -130,10 +130,10 @@ FocusScope { delegate: MuseSoundsListView { width: parent.width - title: modelData.title + sectionTitle: title visible: count > 0 - model: modelData.sounds + model: sounds flickableItem: column @@ -141,8 +141,8 @@ FocusScope { navigationPanel.name: title + "Sounds" navigationPanel.order: index - onGetSoundRequested: { - // todo + onGetSoundRequested: function(soundCode){ + museSoundsModel.openSoundPage(soundCode) } onNavigationActivated: function(itemRect) { diff --git a/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundItem.qml b/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundItem.qml index 2be98ad3cb7f2..da5108d7892d0 100644 --- a/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundItem.qml +++ b/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundItem.qml @@ -72,21 +72,8 @@ Rectangle { Image { id: thumbnailImage - anchors.fill: parent - fillMode: Image.PreserveAspectCrop - - sourceSize: Qt.size(width * Screen.devicePixelRatio, height * Screen.devicePixelRatio) - - layer.enabled: ui.isEffectsAllowed - layer.effect: EffectOpacityMask { - maskSource: Rectangle { - width: thumbnail.width - height: thumbnail.height - radius: thumbnail.radius - } - } } } diff --git a/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundsListView.qml b/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundsListView.qml index 1f020fab37a22..41e798be79bad 100644 --- a/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundsListView.qml +++ b/src/musesounds/qml/MuseScore/MuseSounds/internal/MuseSoundsListView.qml @@ -31,7 +31,7 @@ Column { property alias model: view.model readonly property alias count: view.count - property alias title: titleLabel.text + property alias sectionTitle: titleLabel.text property var flickableItem: null property int headerHeight: titleLabel.height + spacing @@ -39,19 +39,15 @@ Column { property NavigationPanel navigationPanel: NavigationPanel { name: "MuseSoundsListView" direction: NavigationPanel.Both - accessible.name: root.title + accessible.name: root.sectionTitle } spacing: 16 - signal getSoundRequested(var soundId) + signal getSoundRequested(var soundCode) signal navigationActivated(var itemRect) function focusOnFirst() { - // Force the view to load the items. Without this, `view.itemAtIndex(0)` might be null even when `count > 0`, - // causing navigation to break when calling this function from `resetNavigationFocus()` in `PluginsPage`. - view.forceLayout() - view.itemAtIndex(0).requestActive() } @@ -106,9 +102,9 @@ Column { anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter - title: model.title - description: model.description - thumbnailUrl: model.thumbnailUrl + title: modelData.title + description: modelData.description + thumbnailUrl: modelData.thumbnail navigation.panel: root.navigationPanel navigation.row: view.columns === 0 ? 0 : Math.floor(model.index / view.columns) @@ -121,7 +117,7 @@ Column { onGetSoundRequested: { forceActiveFocus() - root.getSoundRequested(model.id) + root.getSoundRequested(modelData.code) } } } diff --git a/src/musesounds/view/musesoundslistmodel.cpp b/src/musesounds/view/musesoundslistmodel.cpp index 9a24d3b79e55c..c77bff74f9851 100644 --- a/src/musesounds/view/musesoundslistmodel.cpp +++ b/src/musesounds/view/musesoundslistmodel.cpp @@ -29,9 +29,10 @@ using namespace mu::musesounds; static const QVariantMap soundInfoToMap(const MuseSoundInfo& soundInfo) { QVariantMap map; + map["code"] = soundInfo.code.toQString(); map["title"] = soundInfo.title.toQString(); map["description"] = soundInfo.description.toQString(); - map["thumbnail"] = QUrl::fromLocalFile(soundInfo.thumbnail.toQString()); + map["thumbnail"] = QString::fromStdString(soundInfo.thumbnail.toString()); return map; } @@ -60,6 +61,18 @@ void MuseSoundsListModel::load() }); } +void MuseSoundsListModel::openSoundPage(const QString& soundCode) +{ + for (const MuseSoundCategoryInfo& category : m_soundsCategories) { + for (const MuseSoundInfo& sound : category.sounds) { + if (sound.code == soundCode) { + interactive()->openUrl(sound.uri.toString()); + return; + } + } + } +} + QVariant MuseSoundsListModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { diff --git a/src/musesounds/view/musesoundslistmodel.h b/src/musesounds/view/musesoundslistmodel.h index 29ca9045385ea..692633e65d1dd 100644 --- a/src/musesounds/view/musesoundslistmodel.h +++ b/src/musesounds/view/musesoundslistmodel.h @@ -27,6 +27,7 @@ #include "async/asyncable.h" #include "modularity/ioc.h" +#include "iinteractive.h" #include "imusesoundsrepository.h" namespace mu::musesounds { @@ -36,6 +37,7 @@ class MuseSoundsListModel : public QAbstractListModel, public muse::async::Async Q_PROPERTY(bool isEmpty READ isEmpty NOTIFY isEmptyChanged) + Inject interactive = { this }; Inject repository = { this }; public: @@ -46,6 +48,7 @@ class MuseSoundsListModel : public QAbstractListModel, public muse::async::Async QHash roleNames() const override; Q_INVOKABLE void load(); + Q_INVOKABLE void openSoundPage(const QString& soundCode); bool isEmpty() const;