diff --git a/src/main.cpp b/src/main.cpp index 5794174..04d46a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ std::string lastPlayingSong = ""; std::string lastMediaSource = ""; std::string currentSongTitle = ""; +utils::SongInfo songInfo; LastFM* lastfm = nullptr; void handleRPCTasks() { @@ -59,6 +60,7 @@ void handleMediaTasks() { while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); auto mediaInformation = backend::getMediaInformation(); + auto settings = utils::getSettings(); if (!mediaInformation) { currentSongTitle = ""; Discord_ClearPresence(); // Nothing is playing rn, clear presence @@ -112,15 +114,15 @@ void handleMediaTasks() { activity.details = mediaInformation->songTitle.c_str(); activity.state = activityState.c_str(); activity.smallImageText = serviceName.c_str(); - std::string artworkURL = utils::getArtworkURL(mediaInformation->songTitle + " " + mediaInformation->songArtist + - " " + mediaInformation->songAlbum); + songInfo = utils::getSongInfo(mediaInformation->songTitle + " " + mediaInformation->songArtist + " " + + mediaInformation->songAlbum); activity.smallImageKey = "appicon"; - if (artworkURL == "") { + if (songInfo.artworkURL == "") { activity.smallImageKey = ""; activity.largeImageKey = "appicon"; } else { - activity.largeImageKey = artworkURL.c_str(); + activity.largeImageKey = songInfo.artworkURL.c_str(); } activity.largeImageText = mediaInformation->songAlbum.c_str(); @@ -140,6 +142,12 @@ void handleMediaTasks() { activity.button1link = buttonText.c_str(); } + std::string odesliUrl = utils::getOdesliURL(songInfo); + if(settings.odesli && songInfo.artworkURL != "") { + activity.button2name = "Show on Song.link"; + activity.button2link = odesliUrl.c_str(); + } + Discord_UpdatePresence(&activity); } } @@ -152,6 +160,8 @@ public: settingsFrame->Raise(); } + void OnCopyOdesliURL(wxCommandEvent& evt) { utils::copyToClipboard(utils::getOdesliURL(songInfo)); } + void OnMenuExit(wxCommandEvent& evt) { settingsFrame->Close(true); } void OnMenuAbout(wxCommandEvent& evt) { @@ -164,10 +174,14 @@ protected: menu->Append(10004, currentSongTitle == "" ? _("Not Playing") : wxString::FromUTF8(currentSongTitle)); menu->Enable(10004, false); menu->AppendSeparator(); + menu->Append(10005, _("Copy Odesli URL")); + if (songInfo.artworkURL == "" || currentSongTitle == "") + menu->Enable(10005, false); menu->Append(10001, _("Settings")); menu->Append(10003, _("About PlayerLink")); menu->AppendSeparator(); menu->Append(10002, _("Quit PlayerLink...")); + Bind(wxEVT_MENU, &PlayerLinkIcon::OnCopyOdesliURL, this, 10005); Bind(wxEVT_MENU, &PlayerLinkIcon::OnMenuOpen, this, 10001); Bind(wxEVT_MENU, &PlayerLinkIcon::OnMenuExit, this, 10002); Bind(wxEVT_MENU, &PlayerLinkIcon::OnMenuAbout, this, 10003); @@ -184,18 +198,17 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, const wxString& name = "textCtrl") - : wxTextCtrl(parent, id, value, pos, size, style, validator, name), - placeholder(""), - showPlaceholder(true), + : wxTextCtrl(parent, id, value, pos, size, style, validator, name), + placeholder(""), + showPlaceholder(true), isPassword((style & wxTE_PASSWORD) != 0) { - Bind(wxEVT_SET_FOCUS, &wxTextCtrlWithPlaceholder::OnFocus, this); Bind(wxEVT_KILL_FOCUS, &wxTextCtrlWithPlaceholder::OnBlur, this); } void SetPlaceholderText(const wxString& p) { placeholder = p; - if (GetValue().IsEmpty() || showPlaceholder) + if (GetValue().IsEmpty() || showPlaceholder) UpdatePlaceholder(); } @@ -203,7 +216,7 @@ protected: void OnFocus(wxFocusEvent& event) { if (showPlaceholder && GetValue() == placeholder) { Clear(); - if (isPassword) + if (isPassword) SetStyleToPassword(); } @@ -225,18 +238,14 @@ private: bool isPassword; void UpdatePlaceholder() { - if (isPassword) + if (isPassword) SetStyleToNormal(); SetValue(placeholder); } - void SetStyleToPassword() { - SetWindowStyle(GetWindowStyle() | wxTE_PASSWORD); - } + void SetStyleToPassword() { SetWindowStyle(GetWindowStyle() | wxTE_PASSWORD); } - void SetStyleToNormal() { - SetWindowStyle(GetWindowStyle() & ~wxTE_PASSWORD); - } + void SetStyleToNormal() { SetWindowStyle(GetWindowStyle() & ~wxTE_PASSWORD); } }; class PlayerLinkFrame : public wxFrame { @@ -387,11 +396,14 @@ public: mainContainer->Add(appsDivider, 0, wxEXPAND | wxALL, 5); wxBoxSizer* settingsContainer; - settingsContainer = new wxBoxSizer(wxHORIZONTAL); + settingsContainer = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* startupContainer; + startupContainer = new wxBoxSizer(wxHORIZONTAL); auto startupText = new wxStaticText(this, wxID_ANY, _("Startup:"), wxDefaultPosition, wxDefaultSize, 0); startupText->Wrap(-1); - settingsContainer->Add(startupText, 0, wxALL, 5); + startupContainer->Add(startupText, 0, wxALL, 5); auto autostartCheckbox = new wxCheckBox(this, wxID_ANY, _("Launch at login"), wxDefaultPosition, wxDefaultSize, 0); @@ -404,8 +416,21 @@ public: utils::saveSettings(settings); }); + auto odesliCheckbox = + new wxCheckBox(this, wxID_ANY, _("Odesli integration"), wxDefaultPosition, wxDefaultSize, 0); + odesliCheckbox->SetValue(settings.odesli); + odesliCheckbox->Bind(wxEVT_CHECKBOX, [](wxCommandEvent& event) { + bool isChecked = event.IsChecked(); + auto settings = utils::getSettings(); + settings.odesli = isChecked; + utils::saveSettings(settings); + }); + settingsContainer->Add(autostartCheckbox, 0, wxALL, 5); - mainContainer->Add(settingsContainer, 0, wxEXPAND, 5); + settingsContainer->Add(odesliCheckbox, 0, wxALL, 5); + startupContainer->Add(settingsContainer); + mainContainer->Add(startupContainer, 0, wxEXPAND, 5); + // settings end this->SetSizerAndFit(mainContainer); diff --git a/src/utils.hpp b/src/utils.hpp index a0cd7ae..c608110 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -35,12 +36,26 @@ namespace utils { }; struct Settings { + bool odesli; bool autoStart; bool anyOtherEnabled; LastFMSettings lastfm; std::vector apps; }; + struct SongInfo { + std::string artworkURL; + int64_t trackId; + }; + + inline void copyToClipboard(const wxString& text) { + if (wxTheClipboard->Open()) { + wxTheClipboard->Clear(); + wxTheClipboard->SetData(new wxTextDataObject(text)); + wxTheClipboard->Close(); + } + } + inline wxIcon loadIconFromMemory(const unsigned char* data, size_t size) { wxMemoryInputStream stream(data, size); wxImage img(stream, wxBITMAP_TYPE_PNG); @@ -125,17 +140,23 @@ namespace utils { return buf; } - inline std::string getArtworkURL(std::string query) { + inline SongInfo getSongInfo(std::string query) { + SongInfo ret{}; std::string response = httpRequest("https://itunes.apple.com/search?media=music&entity=song&term=" + urlEncode(query)); nlohmann::json j = nlohmann::json::parse(response); auto results = j["results"]; if (results.size() > 0) { - return results[0]["artworkUrl100"].get(); + ret.artworkURL = results[0]["artworkUrl100"].get(); + ret.trackId = results[0]["trackId"].get(); } - return ""; + return ret; } + inline std::string getOdesliURL(SongInfo& song) { + return std::string("https://song.link/i/" + std::to_string(song.trackId)); + } + inline void saveSettings(const App* newApp) { nlohmann::json j; @@ -189,6 +210,7 @@ namespace utils { nlohmann::json j; j["autostart"] = settings.autoStart; j["any_other"] = settings.anyOtherEnabled; + j["odesli"] = settings.odesli; j["lastfm"]["enabled"] = settings.lastfm.enabled; j["lastfm"]["api_key"] = settings.lastfm.api_key; @@ -218,6 +240,7 @@ namespace utils { if (!std::filesystem::exists(CONFIG_FILENAME)) { ret.anyOtherEnabled = true; ret.autoStart = false; + ret.odesli = false; saveSettings(ret); return ret; } @@ -229,6 +252,7 @@ namespace utils { ret.autoStart = j.value("autostart", false); ret.anyOtherEnabled = j.value("any_other", false); + ret.odesli = j.value("odesli", false); if (j.contains("lastfm")) { auto lastfm = j["lastfm"];